| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
A keyboard-driven Terminal User Interface for personal task management, built in Rust with Ratatui. LazyTask uses TaskChampion — the same embeddable task engine that powers Taskwarrior 3.x — as its data layer, so it can sync with the official taskchampion-sync-server and stays storage-compatible with Taskwarrior.
LazyTask is standalone: you do not need the task binary installed. The replica lives entirely inside the application's data directory.
Note on cloud sync (S3 / GCS). TaskChampion 3.0.1 ships optional server-aws and server-gcp features for syncing through Amazon S3 or Google Cloud Storage. We deliberately do not enable them — their AWS SDK transitive deps require Rust 1.91+. If you want cloud-bucket sync, bump to rustc 1.91+ and add the features to the taskchampion line in Cargo.toml. The HTTP taskchampion-sync-server path covered above works on Rust 1.90.
Grab the latest tarball for your platform from the GitHub Releases page. Each release ships static binaries for:
| Platform | Tarball |
|---|---|
| macOS Intel | lazytask-x86_64-apple-darwin.tar.gz |
| macOS Apple Silicon (M1/M2/M3) | lazytask-aarch64-apple-darwin.tar.gz |
| Linux x86_64 (glibc) | lazytask-x86_64-unknown-linux-gnu.tar.gz |
| Linux x86_64 (musl, static) | lazytask-x86_64-unknown-linux-musl.tar.gz |
| Windows x86_64 (MSVC) | lazytask-x86_64-pc-windows-msvc.tar.gz |
| Windows x86_64 (MinGW) | lazytask-x86_64-pc-windows-gnu.tar.gz |
| Windows i686 | lazytask-i686-pc-windows-msvc.tar.gz |
Each tarball has a matching .sha256 file for integrity verification.
# Quick install on Linux/macOS:
TAG=v0.1.0
TARGET=x86_64-unknown-linux-gnu # or whatever matches your platform
curl -L "https://github.com/osamamahmood/lazytask/releases/download/$TAG/lazytask-$TARGET.tar.gz" | tar xz
./lazytask --version
sudo mv lazytask /usr/local/bin/ # optionalgit clone https://github.com/osamamahmood/lazytask
cd lazytask
cargo build --release
./target/release/lazytaskThe binary is target/release/lazytask. On first launch it creates a config file at ~/.config/lazytask/config.toml and a SQLite database under the platform's standard data directory (see Data location).
# Run from the source tree
cargo run --release
# Or run the built binary
./target/release/lazytask
# CLI options
lazytask --help
lazytask --config /custom/path/config.toml| Mode | Trigger | Description |
|---|---|---|
| Task List | default | Browse, select, and act on tasks |
| Form | a (add) / e (edit) | Modal for task creation / editing |
| Filter | / | Inline multi-select status / project / tag / search |
| Reports — Dashboard | r | Summary, burndown, project analytics, activity |
| Reports — Calendar | r then c | 3-month view with daily task indicators |
| Sync Config | Shift+S | Modal for entering server URL / client ID / secret |
| Help | F1 | Keyboard reference |
| Key | Action |
|---|---|
| q | Quit |
| Ctrl+C | Quit |
| F1 | Toggle help overlay |
| F5 | Reload tasks from local replica |
| Esc | Cancel / go back / close modal |
| Enter | Confirm / save |
| Key | Action |
|---|---|
| ↑ / ↓ | Move selection |
| a | Open the add-task form |
| e | Open the edit-task form (prefilled) |
| d | Mark selected task done (status → Completed) |
| Delete | Soft-delete selected task (status → Deleted) |
| / | Toggle filter mode |
| r | Open Reports view |
| s | Sync (no-op with hint if not yet configured) |
| Shift+S | Open Sync Config modal |
| Key | Action |
|---|---|
| Tab | Cycle filter sections: Status → Project → Tags → Search |
| ↑ / ↓ | Move within the current section's option list |
| Space | Toggle the highlighted item (multi-select) |
| typing | Filter via free-text search (only in Search section) |
| Backspace | Erase a search character |
| Esc | Exit filter mode (selections remain applied) |
The Status section toggles five flags independently: Pending, Active (started tasks), Overdue (past due, still pending), Completed, Deleted. With nothing selected, all tasks pass through.
| Key | Action |
|---|---|
| ← / → | Move by one day |
| ↑ / ↓ | Move by one week |
| < / > | Previous / next month |
| t | Jump to today |
| c | Toggle back to dashboard |
| Esc | Return to task list |
| Key | Action |
|---|---|
| Tab / ↓ | Next field |
| ↑ (or Shift+Tab) | Previous field |
| ← / → | Cursor within the current text field |
| typing | Edit the active field (description, project, tags, due) |
| Backspace | Erase a character |
| Enter | Commit field, then save the form |
| Esc | Cancel without saving |
Tags use Taskwarrior-style syntax inside the Tags field: +work +urgent to add, -old to remove. Submitting the form with the Tags field empty clears all user tags.
LazyTask speaks the official TaskChampion sync protocol. To use it you need a taskchampion-sync-server reachable over HTTP/HTTPS.
The repo ships a compose.yaml that works with both docker compose and podman compose (or podman-compose):
# 1. Generate a client UUID and put it in .env
echo "CLIENT_ID=$(uuidgen | tr 'A-Z' 'a-z')" > .env
# 2. Bring the server up
docker compose up -d # or: podman compose up -d
# 3. Confirm it's listening
docker compose logs sync-server | tailThe server is now reachable at http://localhost:8810. Use the UUID from your .env file as the Client ID in LazyTask's sync config modal (see below).
To stop:
docker compose down # keep the database
docker compose down --volumes # also wipe the databaseSee docs/sync-server.md for the full reference (custom ports, log levels, multi-device sync, the no-compose docker run form).
If you'd rather not keep compose.yaml around, the equivalent one-shot command is:
CLIENT_ID=$(uuidgen | tr 'A-Z' 'a-z') # macOS BSD uuidgen emits uppercase
podman run -d --name=lazytask-sync -p 8810:8080 \
-e CLIENT_ID=$CLIENT_ID \
ghcr.io/gothenburgbitfactory/taskchampion-sync-server:0.7.1
# Or replace `podman` with `docker` — same flags.
echo "Use this client_id in LazyTask: $CLIENT_ID"-p 8810:8080 maps the container port; pick whatever host port is free.
The image's entrypoint reads CLIENT_ID from the environment, not as a CLI flag. Don't try --allow-client-id <uuid> style invocations — they fail with eval: --allow-client-id: not found.
⚠️ Sync settings are currently held in memory only and not persisted across launches. You must re-enter them each session. Persistent storage is on the roadmap.
src/
├── main.rs # CLI entry, calls into the lib crate
├── lib.rs # Library root (re-exports modules)
├── app.rs # Event loop, terminal setup, App struct
├── config.rs # TOML config load / save
├── taskchampion.rs # The data engine — wraps Replica/Operations
├── data/
│ ├── models.rs # Canonical Task / TaskStatus / Priority types
│ ├── filters.rs # TaskFilter predicate engine
│ ├── cache.rs # In-memory task cache (helper)
│ └── export.rs # JSON / CSV import-export utilities
├── handlers/
│ ├── input.rs # KeyEvent → Action mapping
│ └── sync.rs # SyncHandler with watch-channel status updates
├── ui/
│ ├── app_ui.rs # Top-level UI dispatcher (AppUI)
│ ├── views/
│ │ ├── main_view.rs # Task list with inline filter
│ │ ├── reports_view.rs # Dashboard + Calendar
│ │ └── settings_view.rs # Placeholder
│ └── components/
│ ├── task_list.rs # StatefulWidget with TableState cursor
│ ├── task_detail.rs
│ ├── task_form.rs # Add/edit modal
│ ├── calendar_view.rs # 3-month calendar
│ ├── report_panel.rs # Dashboard panels
│ ├── sync_status.rs # Sync progress overlay
│ └── sync_config.rs # Sync configuration modal
└── utils/
├── formatting.rs # truncate_chars (UTF-8 safe), date helpers
├── helpers.rs
├── keybindings.rs
└── validation.rs
The data flow is simple: App::run polls crossterm events, InputHandler maps each KeyEvent to an Action, and AppUI::handle_action mutates the engine and view state. The engine (TaskChampionIntegration) builds Operations and commits them to the replica via TaskChampion's API. There is no CLI shim, no JSON IPC, no task invocation — every read and write goes through the embedded SQLite-backed replica.
Config lives at ~/.config/lazytask/config.toml and is auto-created on first launch with sensible defaults:
[theme]
name = "catppuccin-mocha"
[theme.colors]
background = "#1e1e2e"
foreground = "#cdd6f4"
primary = "#89b4fa"
secondary = "#f38ba8"
[ui]
default_view = "task_list"
show_help_bar = true
task_list_columns = ["id", "project", "priority", "due", "description"]
refresh_interval = 1000
[taskwarrior]
sync_enabled = false # reserved for future use; not currently read
[keybindings.global]
quit = "q" # reserved; live keymap is currently hard-coded
help = "F1"
refresh = "F5"ℹ️ The [theme], [keybindings.*], and [taskwarrior] sections are accepted by the parser but not all fields are wired up yet. The active keymap is the one documented in Keyboard interface.
The TaskChampion SQLite database is stored in the OS-conventional data directory under a lazytask/ subdirectory (resolved via the dirs crate):
| OS | Path |
|---|---|
| macOS | ~/Library/Application Support/lazytask/ |
| Linux | ~/.local/share/lazytask/ |
| Windows | %APPDATA%\lazytask\ |
Override the data dir is currently only possible at the API level (TaskChampionIntegration::new(Some(path))) — a CLI flag for this is on the roadmap.
# Dev build
cargo build
# Release build
cargo build --release
# Run from source
cargo run
# Format / lint
cargo fmt
cargo clippyThe codebase follows Rust 2021 conventions, uses anyhow for application errors, tokio for the async event loop (single-threaded usage with block_in_place for sync I/O), and serde + toml for configuration.
LazyTask has a 66-test suite across 8 files. All pass on every commit:
# Run the full suite. Of these, 64 always run and the 2 remote-sync tests
# auto-skip cleanly when LAZYTASK_REMOTE_SYNC_URL is unset.
cargo test
# To exercise the live remote-sync tests, point them at a running server:
CLIENT_ID=$(uuidgen | tr 'A-Z' 'a-z')
podman run -d --name=lazytask-sync-test -p 8810:8080 -e CLIENT_ID=$CLIENT_ID \
ghcr.io/gothenburgbitfactory/taskchampion-sync-server:0.7.1
LAZYTASK_REMOTE_SYNC_URL=http://localhost:8810 \
LAZYTASK_REMOTE_CLIENT_ID=$CLIENT_ID \
cargo test
podman rm -f lazytask-sync-test| Suite | Tests | Scope |
|---|---|---|
| tests/unit/config.rs | 7 | Config TOML round-trip |
| tests/unit/filters.rs | 9 | TaskFilter predicate matrix |
| tests/unit/models.rs | 9 | Task JSON parsing (RFC3339, Taskwarrior-compact, Unix timestamps) |
| tests/integration_engine.rs | 8 | CRUD on a real TaskChampion replica in a temp dir |
| tests/integration_sync.rs | 4 | Two replicas synced via LocalServer (file-based) |
| tests/integration_remote_sync.rs | 2 | Live taskchampion-sync-server over HTTP (opt-in) |
| tests/integration_ui.rs | 4 | Render snapshots via TestBackend (incl. unicode + small terminals) |
| tests/integration_tui_input.rs | 23 | End-to-end synthetic-key-event TUI tests for every shortcut |
Maintainers: see docs/releasing.md for the full release procedure. TL;DR — bump the version in Cargo.toml, push a v* tag, and the Build workflow cross-builds binaries for every supported platform and attaches them to a freshly-created GitHub Release.
Issues and pull requests welcome. The todo list above covers the most useful next steps.
MIT — see LICENSE.
| Back | FazBrowse Home | New Git URL |