Build Akiflow
KINDAreplaces $34/mosaves $408/yrback to the verdict
A planner that pulls tasks out of the places they arrive and puts them into one queue you empty, where the engineering is keeping several external systems in sync without duplicating or losing anything, and the design is that every action must be reachable without a mouse.
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 and the sync workers.
Get it nodejs.org, or nvm install 22. open ↗
Verify
node --version prints v22 or higher - accountfree with existing subscriptions
Why Two-way sync behaviour, identifier stability and conflict cases differ per system and none of it can be assumed from documentation.
Get it Accounts on two systems you actually use, with write scopes, and read their sync and identifier documentation before building.
Verify
A task created here appears there and a change there appears here - accountfree
Why Time blocks are real calendar events, and the failure modes only appear against a real calendar with existing meetings.
Get it A calendar with a month of realistic meetings including recurring series.
Verify
Events can be created and the tool's own events are identifiable by stored identifier - have readyfree
Why Triage speed and inbox behaviour are only meaningful against a real backlog with the awkward items you have been avoiding.
Get it Import everything from the systems you actually use, including the fifty things you have been ignoring.
Verify
At least two hundred tasks are imported across two sources - decidefree
Why Every action is keyboard reachable and retrofitting a scheme after building the interface means rebuilding the interface.
Get it Decide the command bar key, the triage keys and the navigation keys before building phase three.
Verify
A written scheme covers every planned triage action
Data model
Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.
- sources(id, kind, account_ref, connected_at, last_synced_at, cursor, disabled_reason) - tasks(id, source_id, external_id, title, notes, due_at, completed_at, deleted_at, updated_at, local_version, remote_version) - inbox(task_id, arrived_at, triaged_at) · the queue you empty - blocks(id, task_id, starts_at, ends_at, calendar_event_id, pinned) · time on the calendar - conflicts(id, task_id, field, local_value, remote_value, detected_at, resolved_at, resolution) - commands(id, name, keys, context) · the command surface, so every action is reachable - A task holds both a local and a remote version, because detecting that both changed is the only way to avoid silently discarding one.
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/planner/tasks.db | Tasks, conflicts and the offline queue. |
SOURCE_TOKENSsecret | required | a keychain reference | Write access to your task systems. Scope narrowly. |
CALENDAR_CLIENT_IDsecret | required | a provider client id | Create and update only the tool's own events. |
SYNC_STALE_HOURS | required | 12 | When a source is surfaced as stale. Silence is the failure mode. |
CONFLICT_RESOLUTION | required | ask | Never last write wins. The rare case is worth a question. |
COMMAND_BAR_KEY | required | Ctrl+K | The entry point to every action. Choose something nothing else claims. |
The build, in order
One task, however many systems
A task from any source is one item here and stays linked to its origin.
Titles collide and change. Identifiers do not.
Files
src/sources.tsSome systems reuse identifiers, which is worse than changing them.
Files
src/sync.tsA disconnected source is not a deletion instruction.
Files
src/sources.ts
done when · tick each as it passeswatch out- Deduplicating imported tasks on title means two genuinely different tasks called follow up merge into one and a note is lost, while the same task retitled at the source arrives as a second item. Key on the source's own identifiers and treat a missing identifier as a source you cannot sync rather than one to guess about.
Writing back without losing anything
A change made in both places is detected rather than silently resolved.
It is the only way to know both changed.
Files
src/versions.tsThey are rare enough that asking is tolerable and losing a note is not.
Files
src/conflicts.tstests/conflict.test.tsA retried completion should not complete twice.
Files
src/write.ts
done when · tick each as it passeswatch out- Last write wins across a two-way sync silently discards whichever change arrived first, and for a task that is usually a note somebody typed and will never think to check again. Track both versions, detect divergence, and ask, since the cases are rare enough that asking is tolerable.
An inbox you can actually empty
Triaging fifty items takes minutes and never a mouse.
One mouse reach per item breaks the rhythm and the model.
Files
src/commands.tsSo actions are discoverable without memorising them.
Files
src/command-bar.tsFast triage produces mistakes and they should be cheap.
Files
src/undo.ts
done when · tick each as it passeswatch out- A triage flow requiring a mouse for any step breaks the rhythm entirely, and a rhythm broken every few items means the inbox does not get emptied and the whole model collapses. Make every action keyboard reachable and measure a realistic triage session rather than assuming.
Tasks on the calendar, honestly
Planned work occupies real time and the calendar reflects it.
Never by title. A rename must not orphan a block.
Files
src/calendar.tsTested by attempting it and expecting a refusal.
Files
tests/foreign-events.test.tsPlanning eleven hours into eight is worth being told about.
Files
src/capacity.ts
done when · tick each as it passeswatch out- Identifying the tool's own calendar events by title or colour means a renamed block becomes invisible to the tool and gets duplicated, or worse, a coincidence of naming makes it modify a real meeting. Store the created event identifier and treat everything else as untouchable.
Working offline and knowing what happenedproduct builder
The planner works on a train and reconciles cleanly afterwards.
A planner that requires a connection is not a planner on a train.
Files
src/offline.tsSilence is the failure mode. Nothing errors when nothing syncs.
Files
src/health.tsSo the data can move rather than being trapped between systems.
Files
src/export.ts
done when · tick each as it passeswatch out- A source whose connection expired quietly stops syncing, so tasks completed here never reach the system your colleagues look at, and nobody finds out until someone asks why a finished task is still open. Show last successful sync per source and alert on staleness, since absence of data raises no error by itself.
That is the whole plan for Akiflow. What it deliberately does not cover is below · check the gaps before you call it a replacement.
- Breadth of source integrations.
- Team assignment and shared projects.
- Automatic scheduling into free time.
- Mobile applications.
- wide integrations
- two-way sync
- keyboard UX
- mobile/desktop apps
- support for API breakage
- Snoozing with a reason, which turns deferral into information rather than avoidance
- A weekly review of what was deferred repeatedly, which is where a backlog gets honest
- Capturing a task from anywhere with a global hotkey, once the inbox flow is proven
Need the files? The project pack on the verdict page hands your agent the whole brief · more tasks & calendar.