Build Timeular
KINDAreplaces $9/mosaves $108/yrback to the verdict
A manual time tracker built on the observation that manual tracking fails for exactly two reasons: switching takes too long and people forget to start. So the entire build is about making the switch instant and making the gaps recoverable.
Before step 1
Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.
- installfree
Why Runs the local application.
Get it nodejs.org, or nvm install 22. open ↗
Verify
node --version prints v22 or higher - installfree
Why Both are platform-specific, and the idle detection in particular differs in what it can see and what permission it needs.
Get it Whatever your desktop framework provides, checking the permission requirement for idle time before designing onboarding.
Verify
A hotkey fires from another application and idle seconds can be read - have readyfree, and it takes two weeks
Why Switch friction, gap frequency and reminder tolerance are all behavioural, and two weeks is roughly how long it takes for a tracker to be abandoned if it is annoying.
Get it Track your real work, including the days you forget entirely, since those produce the gaps the build exists to handle.
Verify
Two weeks exist including at least three days with substantial gaps - decidefree
Why Gaps are detected against working hours and exports are rounded by an agreed rule, and both are enforced in code rather than being preferences.
Get it Write down your working hours per day and the rounding rule you have agreed with whoever receives the exports.
Verify
Both are written down and configured before the first export
Data model
Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.
- activities(id, name, colour, project_id, hotkey, archived_at) - entries(id, activity_id, started_at, ended_at, note, source) · source is switched, filled or edited - gaps(id, from_at, to_at, resolved_by_entry_id, dismissed_at) · untracked time, surfaced rather than ignored - idle_events(at, duration_seconds, resolution) · resolution is discarded, assigned or kept - projects(id, name, client, billable, rate_minor, rate_effective_from) - exports(id, period, rounding_rule, generated_at, path) - An entry records whether it was created by switching, by filling a gap or by editing, because data entered three days later is not the same evidence as data entered at the time.
Environment variables
These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.
| Variable | Needed | Example | Where the value comes from |
|---|---|---|---|
DB_PATH | required | ~/.local/share/timetrack/log.db | Your working time record. |
SWITCH_HOTKEY | required | Ctrl+Shift+T | The global hotkey. Choose one nothing else claims. |
MAX_SWITCH_MS | required | 800 | The threshold that fails the build. Switch cost decides whether tracking happens. |
IDLE_THRESHOLD_SECONDS | required | 300 | When idle time is offered for assignment. Too short and the tracker gets closed. |
WORKING_HOURS | required | 09:00-18:00 | Gaps are detected against these. Evenings are not missing data. |
ROUNDING_RULE | required | none | Applied at export only. The stored entry keeps the actual duration. |
The build, in order
Switching in one keystroke
Changing what you are tracking takes under a second.
The first switch of the day is the one that decides the habit.
Files
src/switcher.tsNotes are added to the entry afterwards.
Files
src/entries.tsA number, not an impression. It is the primary requirement.
Files
src/metrics.ts
done when · tick each as it passeswatch out- A switcher requiring a mouse, a confirmation or a note before it will change activity costs several seconds each time, and at twenty switches a day people stop using it within a fortnight. Make the switch immediate and let the note be added afterwards to the entry that already exists.
The time you forgot to track
Gaps are visible and fillable rather than silently lost.
Not against the clock. Evenings are not missing data.
Files
src/gaps.tsA slow fill flow is the same problem as a slow switch.
Files
src/gaps.tsFilled three days later is weaker evidence than switched at the time.
Files
src/entries.ts
done when · tick each as it passeswatch out- Silently ignoring untracked time produces a week that looks like eleven hours of work, and the person concludes the tracker is broken rather than that they forgot. Surface the gaps, make filling one quick, and let dismissal be an answer so the total is honest either way.
Reminders that do not become noise
You are nudged when it helps and left alone when it does not.
Not during. Interrupting a break is the wrong moment.
Files
src/idle.tsOnce. A timer left running overnight is the case being caught.
Files
src/reminders.tsSeveral dismissals is an answer for the day.
Files
src/reminders.ts
done when · tick each as it passeswatch out- An idle prompt appearing every time you read something for four minutes is a tracker people close by lunchtime. Set the idle threshold from observed behaviour rather than a guess, and stop asking for the day once someone has dismissed several in a row.
Projects, rates and what changed when
Billable time is computed from the rate that applied at the time.
Applying the current rate backwards reprices invoiced work.
Files
src/rates.tssrc/rates.test.tsMid-period changes are the case that gets it wrong.
Files
src/value.tsSo a disagreement is about the rate rather than the arithmetic.
Files
src/report.ts
done when · tick each as it passeswatch out- Applying the current rate to historic entries silently reprices work you already invoiced, so a report run today disagrees with an invoice sent last month and nobody can reconstruct which was right. Store rates with effective dates and always use the one that applied when the work happened.
Export, rounding and the honest totalproduct builder
Exported time matches the agreed rule and the stored data stays exact.
The stored entry keeps the truth.
Files
src/rounding.tsHiding the difference is how billing disputes start.
Files
src/export.tsFilled and switched are different evidence and a client may ask.
Files
src/export.ts
done when · tick each as it passeswatch out- Rounding the stored entry destroys the actual figure, so a client asking what the real time was cannot be answered, and repeated rounding on edits compounds the error. Round at export, show both, and keep the entry exact.
That is the whole plan for Timeular. What it deliberately does not cover is below · check the gaps before you call it a replacement.
- Automatic activity detection from the screen.
- Team tracking and manager visibility.
- Invoicing and payment collection.
- Hardware device integration.
- physical tracker option, mobile apps, calendar integration, team features, and sync
- cross-device history
- team timesheets and payroll
- behavioral datasets
- blocking across every platform
- Estimates against actuals per project, which is where tracking starts improving planning
- A weekly review of gaps and fills, since a rising fill rate means the switching is too slow
- Calendar events offered as gap fills, which covers most meetings automatically
Need the files? The project pack on the verdict page hands your agent the whole brief · more time tracking.