Build Todoist

YESreplaces $7/mosaves $84/yrback to the verdict

0%0 of 20 items done

Saved on this device only. Tick prerequisites first, then work the phases in order · do not start one until the checks above it pass.

A personal task manager on localhost: quick-add that understands 'call Sam tomorrow 4pm #home p2', Today and Upcoming views, recurring tasks that behave on the 31st, full-text search, phone reminders through ntfy, and a CLI for capture from any terminal. One SQLite file you can back up.

estimated effort weekendthe files for this build are in the project pack

RuntimeNode 22 with Express and better-sqlite3Parsingchrono-node for dates, rrule for recurrenceRemindersnode-cron and ntfyHostinglocalhost, or your own network

Before step 1

Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.

  1. installfree

    Why Everything in this build runs on it: the server, the scripts, the tests.

    Get it Download the LTS installer from nodejs.org, or install with your package manager (brew install node, or nvm install 22). Restart the terminal afterwards. open ↗

    Verify node --version prints v22 or higher

  2. installfree

    Why Every step below is a command you type or a file you edit.

    Get it VS Code (code.visualstudio.com), Cursor or Zed. Open a folder for the project and use the editor's built-in terminal. open ↗

    Verify You can open a folder and run a command in its terminal

  3. installfree

    Why History for your code, and the way most hosts deploy.

    Get it Install from git-scm.com or with your package manager, then run git init in the project folder once it exists. open ↗

    Verify git --version prints a version

  4. free

    Why Reminders arrive as push notifications through ntfy. No account needed; a long random topic name is the credential.

    Get it Install ntfy from the App Store or Play Store, subscribe to a topic like tasks-<random 12 chars>, put the topic in .env. Test: curl -d 'hi' ntfy.sh/<topic>. open ↗

    Verify The test message appears on the phone

  5. decidefree

    Why Due dates are plain dates; reminders fire at local times.

    Get it e.g. Europe/Berlin, into .env as TIMEZONE.

  6. decidefree

    Why Both are defensible and users assume different ones. Decide the default now; Phase 4 offers the other per task.

    Get it Default to due-date anchoring for scheduled chores; completion anchoring as a per-task flag for habits.

Data model

Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.

- `tasks`: id, content, notes, project_id, priority (1-3), due_date
  ('YYYY-MM-DD', nullable), due_time (nullable), rrule (nullable), completed_at,
  parent_id (nullable), position, created_at
- `projects`: id, name, color, archived
- `completions`: id, task_id, completed_at

Store `due_date` as a plain date string, not a timestamp. A task is due on a day,
and a timestamp will shift it across midnight for anyone who travels or observes
DST. Keep `completions` separate from `completed_at` so a recurring task has a
history rather than one ever-moving row.

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.

VariableNeededExampleWhere the value comes from
PORTrequired4810Bound to 127.0.0.1 or your LAN address.
DATABASE_PATHrequired./data/tasks.dbSQLite file.
TIMEZONErequiredEurope/BerlinIANA zone for reminders and the digest.
NTFY_TOPICsecretrequiredtasks-a8f3k2m9x1q7Your private topic from the ntfy app.
DIGEST_HOURoptional7Hour of the morning digest in TIMEZONE.

The build, in order

  1. Tasks and projects

    The object model and a list that renders; no parsing yet.

    1. tasks (id, content, notes, project_id, priority 1-3, due_date YYYY-MM-DD, due_time, rrule, anchor, completed_at, parent_id, position, created_at), projects (id, name, color, archived), completions (id, task_id, completed_at).

      terminal
      mkdir tasks && cd tasks && git init && npm init -y && npm pkg set type=module
      npm install express@4 better-sqlite3@13
      mkdir data && cp .env.example .env
    2. Completing writes a completions row. Archiving a project hides its tasks without deleting.

    done when · tick each as it passes
  2. Quick add with natural language

    One line becomes a task with a date, project and priority, previewed before saving.

    1. Strip #project and pN tokens first, then hand the rest to chrono. Feeding those tokens to the parser produces confident nonsense.

      terminal
      npm install chrono-node@2
    done when · tick each as it passes
  3. Views

    Today, Upcoming and per-project, with overdue pinned.

    done when · tick each as it passes
  4. Recurrence

    rrule strings, the anchor rule you decided, and sane behaviour in February.

    1. terminal
      npm install rrule@2
    2. Completing writes a completions row and moves due_date to the next occurrence per the anchor.

    done when · tick each as it passes
  5. Search and subtasks

    FTS5 search and one level of subtasks.

    done when · tick each as it passes
  6. Reminders

    Once per task, surviving restarts, plus a morning digest.

    1. Record notified_at on the task so a restart never re-fires.

      terminal
      npm install node-cron@3
      curl -d 'test' ntfy.sh/$NTFY_TOPIC
    done when · tick each as it passes
  7. CLI and deploy

    Capture from any terminal, backups, a service.

    1. terminal
      npm link
      t add "buy milk tomorrow #home"
    2. README: quick-add syntax with an ambiguous example, the anchor rule and flag, ntfy setup, where the database lives.

      Files README.md

    done when · tick each as it passes
what this build does not replace
after v1, if you want it

Need the files? The project pack on the verdict page hands your agent the whole brief · more tasks.