For context on how my brain works, see Working with an AuDHD Brain.
This is the stack that structures my daily work: taskwarrior for task management, timewarrior for time tracking, and vit as a vim-style interface. Together they form a keyboard-first, terminal-native productivity system.
No Electron apps. No cloud sync sending your data to Silicon Valley. Just plain text, local, and blazing fast.
Why This Stack?
I struggled with task management for years. Todoist, Things, Notion, Obsidian plugins — tried them all, eventually abandoned them all. The problem was always the same: too much friction.
Taskwarrior solves this:
- Speed: Add a task in < 1 second
- Keyboard-only: No mouse needed
- Scriptable: Integration with everything
- Offline: Works always, everywhere
- Plain text: Your data is yours
Installation
macOS
brew install task timewarrior vit
Linux (Debian/Ubuntu)
sudo apt install taskwarrior timewarrior
pip install vit # or via package manager
Arch Linux
sudo pacman -S task timew vit
After installation, run task once to create the config:
task version
# Creates ~/.taskrc and ~/.task/
Taskwarrior Basics
Adding Tasks
# Simple task
task add "Review pull request"
# With tags
task add "Fix login bug" +urgent +backend
# With due date
task add "Report delivery" due:friday
# With project
task add "Write API endpoint" project:acme.api
# All together
task add "Deploy to production" project:acme.api +urgent due:tomorrow priority:H
Viewing Tasks
task list # All pending tasks
task # Same as task list (or task next)
task all # Including completed and deleted
task completed # Only finished tasks
task +urgent # Filtered by tag
task project:acme # Filtered by project
Managing Tasks
task 1 done # Mark as done
task 1 delete # Delete
task 1 modify +tag # Add tag
task 1 modify due:monday # Change due date
task 1 edit # Open in $EDITOR
task 1 info # All details
Starting/Stopping Tasks
task 1 start # Mark as "active"
task 1 stop # No longer active
task +ACTIVE # View active tasks
Projects
Projects in taskwarrior use hierarchical notation with dots:
# Project structure
task add "Something" project:client.project.subproject
# Examples
task add "Design API" project:acme.api
task add "Write tests" project:acme.api.backend
task add "Build UI" project:acme.api.frontend
Viewing Projects
# All tasks in a project (including subprojects)
task project:acme list
# Only specific project
task project:acme.api list
# Project summary
task projects
Context (Project Focus)
Contexts automatically filter your view:
# Define context
task context define work "project:acme or project:byteherder"
task context define acme "project:acme"
# Activate context
task context acme
# Now 'task list' only shows acme tasks
# View context
task context show
# Disable context
task context none
This is exactly what my p function does — see my time-system scripts for the full implementation.
Dependencies
Tasks can depend on each other:
# Task 2 depends on task 1
task 2 modify depends:1
# Multiple dependencies
task 3 modify depends:1,2
# When creating
task add "Deploy" depends:1,2
Blocked Tasks
# View what's blocked
task blocked
# View what's unblocked (ready to do)
task unblocked
# View the dependency chain
task 1 info
# Shows: "blocking: 2, 3" and "blocked by: (none)"
Example Workflow
task add "Design database schema" # Task 1
task add "Build API endpoints" depends:1 # Task 2
task add "Frontend integration" depends:2 # Task 3
task add "Write tests" depends:1 # Task 4
task add "Documentation" depends:2,3,4 # Task 5
task list
# ID Dep Description
# 1 Design database schema
# 2 1 Build API endpoints [blocked]
# 3 2 Frontend integration [blocked]
# 4 1 Write tests [blocked]
# 5 2-4 Documentation [blocked]
task 1 done
# Now tasks 2 and 4 are unblocked
task list
# ID Dep Description
# 2 Build API endpoints
# 3 2 Frontend integration [blocked]
# 4 Write tests
# 5 2-4 Documentation [blocked]
Tags
Tags are flexible labels:
# Adding
task add "Something" +urgent +backend +review
# Filtering
task +urgent list
task +backend +review list # AND
task +backend or +frontend # OR
task -urgent # NOT (exclude)
# Remove tag
task 1 modify -urgent
Useful Tag Conventions
+next # Next to do
+urgent # High priority
+waiting # Waiting on someone else
+review # Needs review
+blocked # Manually blocked (besides depends)
+quick # < 15 minutes
Priorities
# Three levels: H(igh), M(edium), L(ow)
task add "Critical fix" priority:H
task add "Nice to have" priority:L
# Modify
task 1 modify priority:M
# Filter
task priority:H list
Due Dates
Taskwarrior understands natural language:
# Specific
task add "Meeting" due:2026-01-30
task add "Deadline" due:2026-01-30T14:00
# Relative
task add "Soon" due:tomorrow
task add "This week" due:friday
task add "In a week" due:1week
task add "End of month" due:eom
task add "In 3 days" due:3d
# Recurring
task add "Weekly review" due:monday recur:weekly
task add "Standup" due:tomorrow recur:daily until:2026-12-31
Viewing by Due Date
task due:today list # Today
task due.before:friday # Before friday
task overdue # Past due
task +OVERDUE # Same
Reports
Taskwarrior has built-in reports:
task list # Default list
task next # Next tasks (smartly sorted)
task ready # Ready to do (unblocked, not waiting)
task blocked # Blocked by dependencies
task waiting # Waiting on something
task recurring # Recurring tasks
task completed # Finished
task all # Everything
Custom Reports
In ~/.taskrc:
# Minimal list
report.minimal.columns=id,description
report.minimal.labels=ID,Description
report.minimal.filter=status:pending
# Then: task minimal
Timewarrior Integration
Timewarrior automatically tracks time when you start/stop tasks.
Manual Tracking
timew start "coding" +projectX # Start tracking
timew stop # Stop
timew # Current status
timew summary # Today
timew summary :week # This week
timew summary :month # This month
Taskwarrior Hook
Create automatic integration:
# ~/.task/hooks/on-modify.timewarrior
#!/usr/bin/env python3
import json
import subprocess
import sys
old = json.loads(sys.stdin.readline())
new = json.loads(sys.stdin.readline())
if 'start' in new and 'start' not in old:
project = new.get('project', 'untagged')
tags = new.get('tags', [])
subprocess.run(['timew', 'start', project] + tags)
if 'start' in old and 'start' not in new:
subprocess.run(['timew', 'stop'])
print(json.dumps(new))
chmod +x ~/.task/hooks/on-modify.timewarrior
Now task 1 start automatically starts time tracking!
Timewarrior Reports
timew summary :day # Today
timew summary :week # This week
timew summary 2026-01-20 - today # Range
timew tags # All tags
timew summary :week project:acme # Filtered
Correcting Time
# Adjust current tracking
timew modify start -30min # Started 30 min earlier
# Adjust historical entry
timew summary :ids # Find ID
timew modify @1 start 09:00 # Adjust start
timew delete @1 # Delete entry
Vit: Vim Interface for Taskwarrior
Vit is a TUI with vim keybindings. Perfect if you have vim muscle memory.
Starting
vit # Default view
vit project:acme # Filtered view
Navigation
| Key | Action |
|---|---|
j / k | Up / down |
gg | Go to beginning |
G | Go to end |
Ctrl+d / Ctrl+u | Half page down/up |
/ | Search |
n / N | Next/previous match |
Task Actions
| Key | Action |
|---|---|
a | Add task |
e | Edit task (in $EDITOR) |
m | Modify (inline) |
d | Done |
D | Delete |
s | Start/stop |
u | Undo |
Enter | Task info |
Modify Examples
Press m and type:
project:acme # Set project
+urgent # Add tag
-waiting # Remove tag
due:friday # Set due date
depends:1 # Add dependency
priority:H # Set priority
Switching Views
| Key | View |
|---|---|
1 | next |
2 | list |
3 | completed |
4 | blocked |
5-9 | Custom reports |
Command Mode
| Key | Action |
|---|---|
: | Type task command directly |
:q | Quit |
:!command | Shell command |
Example: :project:acme +urgent filters live.
Vit Config
~/.vit/config.ini:
[vit]
default_keybindings = vi
[report]
default_report = next
[color]
color = on
My Daily Workflow
I’ve packaged all my aliases, functions, and scripts into a reusable collection: time-system
# Install
git clone https://github.com/kapott/time-system.git
cd time-system && ./install.sh
source ~/.zshrc
Project-Based Workflow
The scripts expect a two-level directory structure for projects:
~/Documents/Git/ # TASKFUNC_PROJECTS_DIR
├── clientA/
│ ├── project1/
│ └── project2/
├── clientB/
│ └── projectX/
Daily Flow
# Morning
p # Select client/project with fzf
# -> cd's to directory
# -> sets taskwarrior context
# -> shows pending tasks
vit # View tasks in TUI
# Working
tstart # Pick task with fzf, starts timer
# ... work ...
tstop # Stop task + timer
# Throughout the day
tadd "New task" # Adds to current project (or inbox if no scope)
tpl # List tasks for current project
tnext # What's next?
# End of day
dayend # Summary: completed, time tracked, still active
Key Commands
| Command | Description |
|---|---|
p | Select project, cd to dir, set scope |
tstart [id] | Start task + timer (fzf if no id) |
tstop | Stop current task + timer |
tadd <desc> | Add task to current project |
tdone [id] | Complete task (fzf if no id) |
tpl | List tasks for current project |
tctxn | Clear project scope |
weekstart | Weekly review |
dayreview | Daily review |
dayend | End of day summary |
twcurrent | Current tracking status |
See the full command reference for all aliases and functions.
Backup
Tasks live in ~/.task/:
# Simple backup
cp -r ~/.task ~/.task-backup-$(date +%Y%m%d)
# Or with git
cd ~/.task && git init && git add -A && git commit -m "Backup"
Advanced Features
UDAs (User Defined Attributes)
Custom fields:
# In ~/.taskrc
uda.estimate.type=duration
uda.estimate.label=Est
# Usage
task add "Big job" estimate:2h
task estimate.over:1h list
Urgency Tuning
Taskwarrior calculates urgency automatically. Tune it:
# ~/.taskrc
urgency.user.tag.urgent.coefficient=5.0
urgency.user.tag.quick.coefficient=2.0
urgency.blocking.coefficient=8.0
urgency.blocked.coefficient=-5.0
Taskd: Sync Server
If you use taskwarrior on multiple machines (laptop, desktop, server), you want synchronization. That’s what taskd is for — the official taskwarrior sync server.
Why Taskd?
- Multi-device sync: Same tasks on all your machines
- Conflict resolution: Taskwarrior handles merge conflicts
- Offline-first: Works locally, syncs when possible
- Self-hosted: Your data stays yours
Alternatives
Before setting up taskd, consider the alternatives:
| Option | Complexity | Advantage |
|---|---|---|
| Syncthing | Low | Simple, sync entire ~/.task folder |
| Git | Low | Version control, but no real sync |
| Taskd | Medium | Official, proper merge handling |
| Taskchampion Sync Server | Medium | Newer, for Taskwarrior 3.x |
For most people Syncthing is the easiest option. Taskd is for when you want proper sync with conflict resolution.
Installing Taskd Server
Via Package Manager
# Debian/Ubuntu
sudo apt install taskd
# Arch
sudo pacman -S taskd
# macOS (as server, unusual)
brew install taskd
Docker (recommended)
# docker-compose.yml
version: '3'
services:
taskd:
image: andir/taskd
ports:
- "53589:53589"
volumes:
- ./taskd-data:/var/taskd
environment:
- CERT_CN=taskd.example.com
- CERT_ORGANIZATION=MyOrg
docker-compose up -d
Server Configuration
If you install taskd manually:
# Create data directory
export TASKDDATA=/var/taskd
sudo mkdir -p $TASKDDATA
sudo chown $USER:$USER $TASKDDATA
# Initialize
taskd init
# PKI setup (certificates)
cd /usr/share/taskd/pki # or where your pki scripts are
./generate
# Copy certificates
cp client.cert.pem $TASKDDATA
cp client.key.pem $TASKDDATA
cp server.cert.pem $TASKDDATA
cp server.key.pem $TASKDDATA
cp server.crl.pem $TASKDDATA
cp ca.cert.pem $TASKDDATA
# Configure server
taskd config --force client.cert $TASKDDATA/client.cert.pem
taskd config --force client.key $TASKDDATA/client.key.pem
taskd config --force server.cert $TASKDDATA/server.cert.pem
taskd config --force server.key $TASKDDATA/server.key.pem
taskd config --force server.crl $TASKDDATA/server.crl.pem
taskd config --force ca.cert $TASKDDATA/ca.cert.pem
taskd config --force server localhost:53589
taskd config --force log /var/log/taskd.log
taskd config --force pid.file /var/run/taskd.pid
Organizations, Groups and Users
Taskd has a hierarchical structure:
# Create organizations
taskd add org Work
taskd add org Home
taskd add org Freelance
# Groups within organizations (optional)
taskd add group Work backend-team
taskd add group Work devops-team
# Users per organization
taskd add user Work tom
taskd add user Work jan
taskd add user Home tom
# Output: New user key: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
# SAVE THIS KEY!
Important Nuance: No Shared Tasks
Here’s a common misunderstanding: taskd is not meant for sharing tasks between users.
Each user has their own, separate task database. Tom’s tasks are invisible to Jan — even if they’re in the same organization.
What taskd IS for:
- Tom syncs his own tasks between laptop, desktop and phone
- Jan does the same with his own devices
- They share the server infrastructure, not the data
What taskd is NOT for:
- Shared projects
- Team task management
- Assigning tasks to colleagues
Workarounds for team tasks:
| Option | How | Downside |
|---|---|---|
| Shared account | Everyone uses same credentials | No audit trail, who did what? |
| Export/import | task export | ssh colleague task import | Manual, no real sync |
| Different tool | Vikunja, Kanboard, Plane | Not taskwarrior |
My advice: Use taskwarrior for personal tasks, and a dedicated tool for teamwork. Don’t try to make taskd do something it wasn’t designed for.
Generating Client Certificates
cd /usr/share/taskd/pki
./generate.client username
# This creates:
# - username.cert.pem
# - username.key.pem
Copy to your client machine:
ca.cert.pemusername.cert.pemusername.key.pem
Configuring Taskwarrior Client
On every machine where you want to sync:
# Place certificates in ~/.task
cp ca.cert.pem ~/.task/
cp username.cert.pem ~/.task/
cp username.key.pem ~/.task/
# Configure in ~/.taskrc
task config taskd.certificate ~/.task/username.cert.pem
task config taskd.key ~/.task/username.key.pem
task config taskd.ca ~/.task/ca.cert.pem
task config taskd.server taskd.example.com:53589
task config taskd.credentials 'MyOrg/username/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
Starting Server
# Foreground (for testing)
taskdctl start
# As systemd service
sudo systemctl enable taskd
sudo systemctl start taskd
Synchronizing
# First sync (push everything to server)
task sync init
# Normal sync
task sync
Automatic Sync
You can automate sync with hooks or cron:
# ~/.task/hooks/on-exit-sync (automatically after every task action)
#!/bin/bash
task sync
chmod +x ~/.task/hooks/on-exit-sync
Or more conservatively with cron:
# Sync every 30 minutes
*/30 * * * * /usr/bin/task sync >> /tmp/tasksync.log 2>&1
Checking Sync Status
task sync # Sync and show status
task diagnostics | grep -A5 Sync # View sync config
Troubleshooting Taskd
“Certificate fails validation”
# Check if certificates are correct
openssl verify -CAfile ~/.task/ca.cert.pem ~/.task/username.cert.pem
“Could not connect to server”
# Check if server is running
nc -zv taskd.example.com 53589
# Check firewall
sudo ufw allow 53589/tcp
“Sync conflict”
Taskwarrior handles most conflicts automatically. If there are problems:
task sync # View conflict messages
task list # Check if data is correct
Syncthing as Alternative
If taskd is too complex, Syncthing works fine:
# Install Syncthing on both machines
# Sync the ~/.task folder
# Done
# Downside: no real merge, last write wins
# Advantage: works for everything, not just taskwarrior
For my setup I use Syncthing for ~/.task. Simple and works.
Troubleshooting
“No matches”
# Check your context
task context show
# Clear context
task context none
Task disappeared?
task all # View everything
task status:completed # Check completed
task status:deleted # Check deleted
Undo
task undo # Undo last action
Conclusion
Taskwarrior + timewarrior + vit isn’t the easiest setup. There’s a learning curve. But once you’re used to it, it’s unbeatable in terms of speed and flexibility.
The combination of:
- CLI for quick actions
- Vit for overview
- Timewarrior for tracking
- Scripts for your own workflow
…gives you a system that does exactly what you want, without compromises.
Start small. Learn the basics. Add complexity as you need it.
And if it doesn’t work for you — no problem. But for me this is the setup that stays.
task add "Learn taskwarrior" +next due:today
task 1 start
Good luck.
