Focus is a productivity tool designed to keep your computer usage intentional. It helps you stay on track and manage your time effectively, ensuring every session at your desk has a clear purpose rather than drifting into aimless browsing.
The system operates via a background daemon (focusd) that handles time tracking, while a simple CLI (focus) provides the interface for managing your work.
Project site: focus.krabhi.me
Current runtime model:
internal/app/runtime.goorchestrates runtime flow, deadlines, and side effects.internal/domainowns phase transitions (idle,active,break,pending_cooldown,cooldown).internal/schedulerowns deadline execution.internal/storageowns config, presets, history, and socket helpers.
- Intentional Use: Focus gives you a 2-minute grace period to use your computer without an active task. Beyond that, the system will warn you before locking the screen or putting the computer to sleep.
- One Thing at a Time: Only one task can be active at once. When a task finishes, the daemon enforces a cooldown before you can start the next one. Cooldown scales by task length.
- Work Modes:
- short (15 min): No in-task break.
- medium (30 min): No in-task break.
- long (60 min): Break starts at 25 minutes for 5 minutes.
- deep (90 min): Break starts at 45 minutes for 10 minutes.
- Post-Task Action:
longtasks lock the screen at cooldown start by default.deeptasks suspend the machine at cooldown start by default.- Override with
task.long_end_actionortask.deep_end_actionif you want the opposite behavior.
- Break Enforcement (long/deep only):
- You get a reminder 2 minutes before the break starts.
- At break start, Focus locks the screen using the best available session/backend command.
- If the screen is unlocked during break or cooldown, Focus warns once and relocks after the configured
relock_delay.
- Post-Task Cooldown: Cooldown starts only after task completion (not during the in-task break).
- Sleep/Wake Tracking: Focus pauses active task timers when the machine sleeps and resumes them on wake.
- Heads-up Notifications: You’ll receive a notification 5 minutes before a task ends, giving you time to wrap up your work gracefully.
- Status Visibility:
focus statusshows cooldown state, active task remaining time, and break-specific details including relock countdown when applicable.
flowchart TB
Idle(["Idle"])
Active(["Active"])
Break(["Break"])
Cooldown(["Cooldown"])
Lock["Lock screen"]
Idle -- focus start --> Active
Active -- long/deep break time --> Break
Break -- break ends --> Active
Active -- task completes --> Cooldown
Cooldown -- cooldown ends --> Idle
Active -- cancel in grace period --> Idle
Idle -- no task for 2m --> Lock
Break -- unlock during break --> Lock
Cooldown -- unlock during cooldown --> Lock
Idle means no task is running and no cooldown is active. Break only applies to long and deep tasks. If the user unlocks during break or cooldown, Focus relocks after relock_delay. Screen locking is an action, not a separate state.
For now this is only available in Linux. It depends on a systemd user session and the desktop tools below.
Runtime dependencies used by focusd:
loginctlorxdg-screensaverfor screen lockcinnamon-screensaver-commandorgnome-screensaver-commandfor unlock, if your desktop exposes onesystemctlorloginctlfor sleep/suspend on long and deep task cooldown startnotify-sendfor desktop notificationspaplay,pw-play,aplay,mpv,ffplay,cvlc, ormpg123for task-ending sound (assets/task-ending.mp3)focus-eventshelper binary (installed privately withfocusd)
Environment-specific notes:
- Lock and unlock are best-effort and depend on the desktop/session providing a supported command.
- Sleep is best-effort and uses the first available suspend command for your session.
- Sound playback is best-effort and uses the first available audio tool.
systemctl --useris needed only if you use the user service install path.
Runtime observability:
- Set
FOCUS_TRACE_FLOW=1to log runtime flow actions and core events.
CLI output is colorized automatically in interactive terminals. Set NO_COLOR=1 or redirect output to disable colors.
Build everything with:
make buildRun the daemon and client from the built binaries:
./dist/focusd
./dist/focus status
./dist/focus historyfocus statusshows the current task, cooldown, or break state.focus history --allshows every completed task in the persisted history file.focus reloadreloads daemon configuration from disk.focus config <key>shows the current value and default.focus config <key> <value>updates one config value and reloads the daemon.focus config alert.repeat_countcontrols how many times the task-ending sound can play;0means no sound.focus config task.long_end_actioncontrols whetherlongtaskssleeporlockwhen cooldown starts.focus config task.deep_end_actioncontrols whetherdeeptaskssleeporlockwhen cooldown starts.focus doctorprints dependency, socket, daemon IPC, and service health checks.focus versionprints the installed binary version.focus updateupgrades to the latest release.focus update --version v0.1.4upgrades to a specific release.focus uninstallasks for confirmation three times before removing the installed binaries and user service.systemctl --user status focusd.servicechecks whether the daemon service is running.
Avoid running go run cmd/focusd/main.go directly. Use the package path instead if you want to run from source:
go run ./cmd/focusd
go run ./cmd/focus statusInstall latest release (GitHub):
curl -fsSL https://focus.krabhi.me/install.sh | shInstall a specific version:
curl -fsSL https://focus.krabhi.me/install.sh | sh -s -- --version v0.1.0Manual (recommended for audit): download install.sh, review it, then run it.
Install from local source checkout:
./scripts/install-local.shThis installs:
focusto~/.local/binfocusdandfocus-eventsto~/.local/libexec/focus- sound assets to
~/.local/share/focus/assets focusd.serviceto~/.config/systemd/user/focusd.service
Manage service manually if needed:
systemctl --user daemon-reload
systemctl --user enable --now focusd.service
systemctl --user status focusd.serviceUninstall:
focus uninstallFor a custom install prefix:
focus uninstall --prefix /custom/prefixCheck installed version:
focus versionUpdate to the latest release:
focus updateUpdate to a specific release:
focus update --version v0.1.2For custom installs:
focus update --prefix /custom/prefixNote: release updates currently target linux/amd64, matching the published release assets.
Focus can load runtime settings from JSON config.
Full reference:
-
Default path:
~/.config/focus/config.json -
Override config path:
FOCUS_CONFIG=/path/to/config.json
Apply changes without restarting daemon:
focus reloadOptional install flags:
./scripts/install-local.sh --prefix /custom/prefix
./scripts/install-local.sh --no-build
./scripts/install-local.sh --no-systemdFor a source checkout, use:
./scripts/uninstall.shCurrent prebuilt release target: linux/amd64.
After pushing a version tag, verify workflow + release assets:
make check-release VERSION=v0.1.0For private repos, use a token with repo/actions read permissions:
GITHUB_TOKEN=your_token_here make check-release VERSION=v0.1.0This repo has a tag-triggered GitHub Actions workflow at:
.github/workflows/release.yml
When a tag matching v* is pushed (example: v0.1.1), the workflow:
- Builds release artifacts via
scripts/package-release.sh. - Publishes a GitHub Release.
- Uploads
focus_<tag>_linux_amd64.tar.gz. - Uploads
checksums_<tag>.txt.
git checkout main
git pull --ff-only
git tag v0.1.1
git push origin main
git push origin v0.1.1make check-release VERSION=v0.1.1curl -fsSL https://focus.krabhi.me/install.sh | sh -s -- --version v0.1.1 --no-systemd- Current prebuilt target is
linux/amd64. - If tag was pushed by mistake, delete it locally and remotely:
git tag -d v0.1.1
git push origin :refs/tags/v0.1.1