User Guide¶
Everything a user needs, in the order you'll meet it.
Contents¶
- Quickstart
- The Pomodoro cycle
- Tasks
- Projects (and the Inbox)
- Sprints
- Lunch breaks
- The session-end modal
- Pre-end "ending soon" warning
- Kanban board
- Stats — daily/weekly/monthly + project drill-down
- History
- Presets
- Themes
- Notifications
- Hooks (shell commands on phase change)
- Resume on restart
- Exports
- Data locations
Inline task syntax¶
The task input on Dashboard and Kanban understands a small shorthand:
| Token | Meaning |
|---|---|
#tag |
Add a tag (multiple allowed) |
@project |
Assign to project (auto-created if it doesn't exist). First @ wins. |
!sprint |
Assign to a sprint (auto-created as a 14-day shell if new). First ! wins. |
~N |
Estimated pomodoros. First ~N wins. |
If a project filter is active when you add a task, the new task inherits that project automatically — you don't need @project for every entry.
Projects (and the Inbox)¶
Projects are 1:N containers for tasks: a task belongs to exactly one project, or to none (the Inbox). Tags are still a separate thing — orthogonal labels like #urgent or #docs.
- Pick a project filter:
Shift+Popens a modal listing All / Inbox / each project. The choice persists across restarts. - Manage projects: press
5for the Projects screen — add (n), rename (r), cycle color (c), archive (a), delete (d). - Project badges appear on every task card and dashboard list. Inbox renders as a grey badge.
- Per-project stats: with a project filter active, the Stats screen recolors its heatmap and adds a drill-down (total / month / week / avg per day / estimate ratio / day-of-week distribution / idle-gap warning).
Sprints¶
Sprints are time-boxed containers for tasks within a project. Each sprint has a name, start/end date, optional goal and pomban target.
- Manage sprints: press
6for the Sprints screen —nadd,aactivate,ccomplete,xcancel,ddelete,eedit target,gedit goal. - Activate (
a): only one sprint per project can be active at a time. Picking a sprint as the filter (Shift+For via the screen) restricts Kanban to its tasks. - Burndown: when a sprint filter is active, the Stats screen renders a burndown sparkline (remaining 🍅 over time vs ideal linear pace).
- Sprint export:
pomban sprint export <id>prints a markdown report (goal, dates, shipped vs not, retrospective if set).
Lunch breaks¶
A lunch (or coffee walk, school pickup, …) is a real recurring interruption that deserves to be timed.
- Press
Shift+Lat any time to start a long pause. It interrupts the current phase, logs an interruption with reasonlunch, and starts a freshlong_pausesession. - Long pauses do not consume a Pomodoro cycle and are excluded from focus totals on the Stats screen.
- If
[breaks].lunch_window_startand…_endare configured, the session-end modal (after a focus phase) adds an[l] take lunchbutton when the current time is inside the window and no lunch has been logged today. - Configure duration via
[breaks].lunch_minutes(default 45).
Install¶
The quickest path is the Makefile:
make install # pip install -e . (adds the `pomban` command)
make run # or: python -m pomban
make install-dev # editable install + pytest, for hacking on it
make test # run the suite
make help # list all targets
Requires Python ≥ 3.11. The only runtime dependency is Textual.
Quickstart¶
- Type a task title in the input box at the bottom right; press Enter.
- Press j/k to highlight the task; press Enter (or s) to start a focus session on it.
- Work until the timer hits zero. The session-end modal pops up and asks if you completed the task.
- Pick c (yes, mark done) or k (not yet, keep in Doing). A break starts automatically.
That's the whole loop. Everything else is polish.
The Pomodoro cycle¶
Phases run on a strict cycle:
┌────────────────────────────────────────────┐
│ │
▼ │
┌─────────┐ s ┌─────────┐ timer ┌─────────┐ │
│ IDLE │ ─────▶ │ FOCUS │ ────▶ │ modal │──┘ c/k → break
└─────────┘ │ 25min │ ends └─────────┘
└─────────┘
After every N focus sessions (default N=4), the short break is replaced by a long break (15min by default). N is configurable.
Phase control¶
| Key | What it does |
|---|---|
s / Space |
Start (from idle) / pause / resume. |
r |
Reset the timer to idle. Logs the current session as incomplete. |
Shift+S or S |
Skip the current phase — immediately jump to the next one. Counts toward the long-break tally. |
You only ever explicitly start focus sessions. Breaks come automatically (or via skip).
Tasks¶
Tasks have one of three statuses: todo, doing, done. They're stored in a single SQLite table and shared between Dashboard list and Kanban board — same data, two views.
Adding tasks¶
On the Dashboard or Kanban, press n to focus the input box. Type the title and press Enter.
You can include inline tags with #:
This creates a task with title Write quarterly report and tags docs,urgent. Tag colors are deterministic — the same tag always gets the same color across cards.
Task lifecycle¶
[ ] todo ──[start focus]──▶ [~] doing ──[c on modal]──▶ [x] done
▲ │
└────────[H on Kanban]─────────┘
- Dashboard list hides
donetasks. To see them, switch to Kanban (2) or History (4). - Starting a focus session on a
todotask auto-moves it todoing. - Marking done from the session-end modal moves it to
done.
Manually changing status¶
- Dashboard:
cmarks the selected taskdone. There's no Dashboard shortcut to move done → todo. - Kanban:
Shift+H/H/</,andShift+L/L/>/.move the focused card across columns in either direction. So to "un-complete" a task: open Kanban → navigate to Done column → press,(move left) to send it back to Doing.
Reordering and deleting¶
| Key | Action | Where |
|---|---|---|
d / x |
Delete focused task / card | Dashboard, Kanban |
Shift+J/J/] |
Move card down within column | Kanban |
Shift+K/K/[ |
Move card up within column | Kanban |
The session-end modal¶
When a focus session reaches zero, the engine does not auto-advance. A modal pops up:
┌─────────────────────────────────────────────┐
│ 🍅 Focus session complete │
│ │
│ You worked on Write report. │
│ Did you finish it? │
│ │
│ c Yes — mark done & take a break │
│ k Not yet — keep in Doing & take break │
│ e Extend focus (5/0/+ = +5/+10/+15) │
└─────────────────────────────────────────────┘
| Key | Action |
|---|---|
c |
Mark task done, end DB session with completed=1, advance to break. |
k |
Keep task in Doing, end DB session with completed=1, advance to break. |
e then 5/0/+ |
Extend focus by +5 / +10 / +15 min. Same DB session row stays open; planned_seconds is bumped. |
Esc |
Dismiss without choosing. Engine stays paused; you can pick later. |
For break-end the modal is simpler: Enter to start the next focus, or e to extend the break.
What "extend" does exactly¶
- Engine: clears
awaiting_decision, adds N seconds toremaining, resumes the same phase. - DB: bumps
sessions.planned_secondson the still-open row. No new session row.
So a 30-minute focus that you extended twice by 5 min shows as a single 40-minute session in History.
Pre-end "ending soon" warning¶
30 seconds before any phase ends, the app fires one soft cue:
- Terminal bell
- Brief screen-flash
- In-app toast:
30s left on focus
No desktop popup (too noisy). The warning fires exactly once per phase — pause/resume doesn't re-fire it.
Threshold is configurable: warning_seconds in [timer]. Set to 0 to disable.
Kanban board¶
Press 2 to switch to Kanban.
┌─ To Do (3) ──┐ ┌─ Doing (1) ──┐ ┌─ Done (12) ─┐
│ [ ] Write… │ │ [~] Refactor │ │ [x] Ship… │
│ [ ] Email… │ │ │ │ [x] Review │
│ [ ] Call… │ │ │ │ ... │
└──────────────┘ └──────────────┘ └─────────────┘
The active column is highlighted with an accent border, and the footer keymap
changes with it: To Do/Doing offer Focus and Move →, while Done offers
Reopen (o, back to To Do) instead. Cycle columns with h/l or Tab/Shift+Tab.
Navigation¶
| Key | Action |
|---|---|
j / k |
Move cursor up/down within current column |
h / l |
Move between columns (also Tab / Shift+Tab) |
Moving cards¶
| Key | Action |
|---|---|
Shift+H / H / < / , |
Move card to previous column |
Shift+L / L / > / . |
Move card to next column |
Shift+J / J / ] |
Reorder card down within column |
Shift+K / K / [ |
Reorder card up within column |
Multiple aliases exist for the
Shift+-modified keys because some terminals (Kitty, especially) intercept them. IfShift+Hdoesn't fire, tryHalone, or<.
Card operations¶
| Key | Action |
|---|---|
n |
New card — adds to the currently focused column |
Enter / s |
Start a focus session on the focused card |
c |
Mark card done (moves to Done column) |
e |
Edit card — title, tags, estimate, project, due date, priority |
i |
Card detail — full notes + metadata (read view; e to edit, p to cycle priority) |
d / x |
Delete card |
/ |
Filter the board by text or #tag (Esc clears) |
1 |
Switch to Dashboard |
Cards sort by priority then due date (overdue renders red). Set per-column
WIP limits in [kanban] to flag overloaded columns. In visual-select mode (v,
then Space to pick), the move / done / delete keys and g (add tag) act on the
whole selection.
Starting a focus from Kanban automatically switches you to Dashboard so the timer is visible.
Stats¶
Press 3. Shows:
- Last 7 days heatmap — focus minutes per day, encoded as unicode block density (
·░▒▓█). - Last 30 days heatmap — same encoding over a wider window.
- Top tasks — 5 tasks with the most focused time, summed across all sessions.
- Summary:
- Sessions today + minutes today
- Current streak (consecutive days with ≥1 completed focus session)
- 30-day total
- Avg interruptions per focus session
Data refreshes on screen entry.
History¶
Press 4. Paged table of the last 100 sessions:
| Column | What |
|---|---|
| When | YYYY-MM-DD HH:MM of start |
| Kind | focus/short_break/long_break |
| Planned | Original duration including any extends |
| Actual | Real elapsed time |
| Done | ✓ if completed=1, · otherwise |
| Interr. | Pause count during the session |
| Task(s) | Comma-joined task titles linked via session_tasks |
A session is completed=1 when the user picked c or k on the modal (or the simpler break modal). completed=0 means it was reset, skipped, or discarded on resume.
Presets¶
A preset bundles focus_minutes, short_break_minutes, long_break_minutes, and cycles_before_long_break.
Press p to open the preset picker. Selecting one applies on the next session (not the currently running one). To get a preset immediately: press r to reset, p to pick, s to start.
Built-in suggested presets (see configuration.md for the file format):
| Name | Focus / Short / Long | N cycles |
|---|---|---|
| classic | 25 / 5 / 15 | 4 |
| deep-work | 50 / 10 / 30 | 3 |
| sprint | 15 / 3 / 10 | 4 |
| ultra-deep | 90 / 15 / 30 | 2 |
| micro | 5 / 2 / 5 | 4 |
| reading | 45 / 10 / 20 | 3 |
Themes¶
Press t to cycle themes:
nord → gruvbox → dracula → catppuccin-mocha → tokyo-night → textual-dark → textual-light → nord ...
The active theme is written to config.toml (ui.theme) so it persists across restarts.
Notifications¶
When a phase ends, up to three channels fire (each independently togglable in config):
- Desktop notification via
notify-send(Linux). - Sound via
paplay/aplay/ffplay— first one available. Default sound is/usr/share/sounds/freedesktop/stereo/complete.oga. - In-TUI bell + flash — works in any terminal, no system deps.
Configurable under [notifications] (see configuration.md).
Working-hours quiet¶
Set working_hours_start and working_hours_end under [breaks] to define your active window. Outside it, the desktop and sound channels suppress automatically; the in-TUI bell still fires so you don't miss a phase change. The context header surfaces a quiet chip while suppression is active.
Blocker capture¶
Hit b during a focus session to drop a one-line blocker against the live session. The timer keeps running. Blockers show up on the Today digest and per-session in History.
Session notes¶
When a focus phase ends, the session-end modal includes a free-text note field. The note attaches to the session row and renders inline on the History screen — useful for "what did I actually do in those 25 minutes" recaps.
Today digest¶
Press 7 to open the Today screen — a single-pane recap of the current day: total focus sessions, top tasks, interruption count, blocker list. Bound to the 7 key so the digit row matches the planning ladder (1 dashboard → 7 today).
Per-tag analytics¶
The Stats screen (3) ships a per-tag analytics panel powered by minutes_per_tag. It lets you see where time actually went — #deep-work vs #meetings vs #admin — over the current bucket window.
Hooks¶
Run any shell command on phase transitions. Configure under [hooks] in config.toml:
[hooks]
on_focus_start = "notify-send 'Do not disturb'"
on_focus_end = "notify-send 'You can chat again'"
on_break_start = "spotify play \"Focus playlist\""
on_break_end = "spotify pause"
Each command runs in sh -c … with these env vars:
| Var | Value |
|---|---|
POMODORO_PHASE |
focus / short_break / long_break |
POMODORO_EVENT |
start / end |
POMODORO_TASK_TITLE |
The active task's title (empty if no task) |
Hooks are fire-and-forget — they never block the UI. stdout/stderr are appended to ~/.local/state/pomban/hooks.log. Failures (missing binary, non-zero exit) are silent and logged.
For more powerful in-process integration, use plugins instead.
Resume on restart¶
If you quit (q) or crash during a focus session, the next launch shows:
┌─────────────────────────────────────────────┐
│ Resume previous focus session? │
│ │
│ task: Write report │
│ remaining: 12:34 │
│ │
│ y resume — n discard (logs as incomplete)│
└─────────────────────────────────────────────┘
State stored in config_kv: pending_session_id, pending_remaining_seconds, pending_phase, pending_task_id.
y→ engine jumps back into the same phase with the savedremaining; same DB session row is reused.n→ DB session closed withcompleted=0; pending state cleared.
Caveat: only the focus phase is currently persisted. If you quit during a break, the break is forgotten (you'll start fresh next launch).
Exports¶
pomban export # last 7 days, markdown
pomban export --since 30d # last 30 days
pomban export --since 14d > review.md
pomban export --since 7d --format csv # raw CSV
pomban export --since 7d --format json # JSON for downstream tooling
pomban export --since 30d --format markdown # grouped by project/sprint
pomban sprint export <sprint-id> # per-sprint report
The grouped-markdown format buckets sessions by project → sprint → task so a weekly review reads like a digest. CSV and JSON give you the raw session rows.
Produces a Markdown document:
# Pomodoro review — 2026-05-18 → 2026-05-24
- **32** focus sessions (13h 20m)
- Streak: **5** day(s)
- Avg interruptions / focus: **0.6**
## Top tasks
- Write report — 4h 10m
- Refactor auth — 3h 25m
- ...
## Daily breakdown
| Date | Minutes |
|------|---------|
| 2026-05-18 | 75 |
| 2026-05-19 | 100 |
| ...
Data locations¶
| Path | Contents |
|---|---|
~/.local/share/pomban/pomban.db |
All tasks, sessions, interruptions, KV |
~/.config/pomban/config.toml |
User configuration |
~/.local/state/pomban/hooks.log |
stdout/stderr from shell hooks |
~/.local/state/pomban/plugins.log |
Errors from in-process plugins |
Override locations by setting $XDG_DATA_HOME, $XDG_CONFIG_HOME, $XDG_STATE_HOME — the app respects the XDG Base Directory spec.
To wipe and start fresh: