# cassidoo/todometer: a to-do app whose real engineering is the installer

> todometer is an MIT licensed Electron and React task app with a progress bar, and the substance of the repository is not the task list but what surrounds it: a protocol handler, a local REST API, an MCP server shipped as a bundled resource, a browser clipper in a second repository, and an installer configuration that makes specific choices about who installs it and where.

**cassidoo/todometer** — A simple task app with a progress bar

- Repository: https://github.com/cassidoo/todometer
- Website: https://cassidoo.github.io/todometer/
- Stars: 2,132 · Forks: 308
- Language: JavaScript
- License: MIT
- Published: 2026-09-30 · Updated: 2026-09-30 · Language: en
- Canonical page: https://hysenlabs.com/projects/cassidoo-todometer

## Capture arrives from three different directions

The application itself is described in one line, a task app with a progress bar, and the feature list is short: add, complete, pause and delete items, drag and drop to reorder them or move them between groups, and a daily auto-reset with optional notifications and reminders. A settings drawer opens from a menu at the bottom of the window and holds notification preferences, the location of the data store, display toggles for the reset and copy buttons, and the switches for the integration layer.

What is interesting is how items get into that list. There are three separate routes, and each one is a different design decision.

The first is a protocol handler. The build configuration registers a custom URL scheme, and the feature is described as adding todos through `todometer://add?text=...` URLs. Registering a scheme is a per-platform operation, which is why it appears in the installer configuration rather than in application code, and the payoff is that any program on the machine can open a task with a link. That is the difference between a to-do list and a target other tools can push to.

The second route is a browser extension in a separate repository, described as a web clipper that adds tasks with a single click. Keeping it out of this repository is the right call, since a browser extension has its own release cycle and its own store, and merging the two would mean every clipper release also triggered an app release.

The third is the one that makes this project worth reading, and it is covered in its own section below.

## A local REST API and an MCP server, shipped as a bundled file

A to-do application with an HTTP API and a Model Context Protocol server is not a to-do application. It is a small automation target, and the packaging shows how the author expected that to be used.

Both live behind the same toggle in the settings drawer, described as a local REST API and MCP server for external integrations, with a setup document linked from the feature list. The framing in that document link is telling: control todometer from scripts, shortcuts or AI assistants. Three consumers, one application, no account and no server in the middle.

The build configuration explains the deployment shape. The MCP entry point lives in the source tree as a module file, and it is copied into the packaged application as an extra resource under a shortened path:

```json
"extraResources": [
    {
        "from": "src/mcp/index.mjs",
        "to": "mcp/index.mjs"
    }
]
```

Copying it out of the bundle rather than compiling it in is a meaningful choice. As a resource, the file exists on disk next to the application, so an agent configuration can point at an absolute path and launch it, the same way you would point at any command line tool. Compiled into the main process instead, it would only be reachable from inside the app, and the integration would be useless to the thing it is meant to serve. The module extension is kept, so the file is loadable as a module by whatever runtime the agent brings with it.

There is a security question here that the project does not answer in the README, and a reader should work it out before switching the feature on. A local API that can add and complete tasks is a local API that can be driven by anything running as the same user, and MCP clients are configured with exactly the kind of absolute path shown above. On a single-user machine with a trusted local client that is a reasonable trade. On a shared machine, or with an agent that will act on instructions from a web page, it is worth knowing what the API exposes before enabling it.

## The Windows installer chooses per-user, assisted and relocatable

The installer configuration is where a small app makes its public commitments, and this one is unusually specific about each platform. The Windows target is a single NSIS installer, and three settings on it describe the whole user experience:

```json
"nsis": {
    "oneClick": false,
    "allowToChangeInstallationDirectory": true,
    "perMachine": false
}
```

Taken one at a time, these are three deliberate choices. Turning off the one-click installer means the user sees a wizard rather than a progress bar that ends with the app appearing, which is the default for a reason but is not what you want if your build is ever distributed outside your own machine. Allowing the installation directory to be chosen is what makes the install relocatable, and it matters for anyone keeping utilities on a separate drive or in a portable tools directory. And per-machine being off means the installer defaults to a per-user location, so nothing needs administrator rights and nothing touches a system directory.

That last choice has consequences beyond convenience. A per-user install lives in the user's own profile, so it can be removed by deleting a directory, and it cannot service all accounts on a shared workstation from one installation. For a task list that is unambiguously the right default.

The macOS configuration is equally explicit, with a utilities category, an icon file, two output formats and a hardened runtime flag:

```json
"mac": {
    "category": "public.app-category.utilities",
    "icon": "assets/mac/icon.icns",
    "target": [
        "dmg",
        "zip"
    ],
    "hardenedRuntime": true,
    "gatekeeperAssess": false,
    "notarize": false
},
"dmg": {
    "sign": false
}
```

Shipping both a disk image and a zip is the accommodation for anyone who dislikes mounting an installer, and the utilities category is what determines where the app sorts in the applications folder.

## Notarization appears three times in three different states

The macOS block above is worth pulling apart, because the same question is answered three times in three different ways and the answers are not consistent.

The mac target sets the hardened runtime on and sets notarization off. The disk image block sets signing off for the image. And the top level build configuration points an after-sign hook at a notarization script. So the same file both disables notarization and schedules a script whose name says it notarizes.

There is a reasonable reading in which all three are consistent. The hardened runtime setting is a build flag that changes how the binary is linked, and it is frequently enabled even for locally distributed builds. The disk image signing flag concerns the container rather than the application inside it. And the after-sign hook is the mechanism by which notarization would actually be performed, so turning off the built-in path and supplying a script instead is a common way to do custom notarization with a different credential flow. On that reading nothing is wrong, and the configuration is a deliberate choice to handle signing outside the default path.

The reason to flag it anyway is that the reading requires you to know the tool's defaults. A reader who does not will see notarization disabled in two places and a notarization script in a third, and the natural conclusion is that macOS distribution is unsigned. For anyone forking this repository to ship to other people, that is a question worth answering in the release documentation before you publish, because a downloaded app that is neither signed nor notarized is one users have to clear a gate for, and a developer application distributed to the public is exactly the case that gate exists for.

The other platform entry is the shortest of the three: Linux is a single AppImage with a 256 pixel PNG icon, which is the format that runs without a system install and is the only one of the three that needs no packaging decisions at all.

## Daily reset is the idea, and the data store is a movable file

The one design choice that distinguishes this from a generic task list is the daily auto-reset, and the settings that surround it are what make it usable rather than annoying.

A reset that empties your list every morning is a feature if you want a fresh start and a bug if you do not, so the notifications and reminder frequency are configurable. The reset is also accompanied by a copy button, and the display options let you hide the reset and copy buttons entirely, which is the setting a user reaches for once they trust the app and want the window to be nothing but the list and the bar.

The data store is treated as a first-class citizen. The settings drawer exposes its location, and moving your database anywhere is presented as a normal operation rather than a hidden path. For an application that keeps everything locally, that is the setting that decides whether you can back the app up with the rest of your files, sync it, or run two instances against separate lists. A local-first app that will not tell you where its data lives is a local-first app you cannot move.

The progress bar deserves a mention too, since it is the only part of the interface described in the project description. In a list that resets daily, a visual measure of completion is the entire feedback loop, and the reason the app exists rather than being a row in some other list. The screenshot referenced in the README is the only visual documentation, which tells you how little surface there is to document.

## What the file listing reveals about how it is built

The repository contents say more about the project's habits than the README does, and a few entries are worth naming.

There is a design source file at the root with a `.afdesign` extension, which is the working format of a vector editor, sitting next to the asset directories the build references. The icon is therefore drawn in the repository rather than exported once and deleted, which for a small app maintained by one person is a reasonable thing to keep and an unusual thing to commit.

The build output is a TypeScript or JavaScript project with an explicit output directory per process. The manifest declares the main entry as a CommonJS file inside a package marked as an ES module, which is the shape Electron needs when the main process is bundled for Node while the rest of the toolchain is native modules. The files list included in the package is the interesting one: the entire `node_modules` tree, the three built output directories for the main process, the preload script and the renderer, one audio asset, one vector logo, and the manifest itself. Shipping all of `node_modules` is the standard consequence of using a bundler that does not inline dependencies, and it is the reason the packaged app is large relative to the code.

The development script does not use a standard runner. It invokes a small script of the author's own under the source directory, which is a watcher that then drives the rest of the pipeline, and the build script runs the main, preload and renderer builds as parallel jobs rather than in sequence. Parallel builds on three process targets is a small thing that matters on every incremental change.

Release documentation lives at the root in a file with a different name from the contributing guide, and the pack scripts are broken out per platform, producing a disk image and a zip on macOS, an NSIS installer on Windows and an AppImage on Linux, with a combined target for all three. The GitHub releases are where the binaries land, and the most recent three are 3.0.1 and 3.0.2 on the same day in May 2026 followed by 3.0.3 in August.

## Conclusion

todometer is a good project to read if you want to see what shipping a small desktop app actually involves, since the task list is trivial and the packaging configuration is where the decisions live. Install it if you want a daily-reset task list with a progress bar and are willing to let a local API sit on your machine. Do not adopt it as a component or a platform, since the API and the MCP server exist to serve one app rather than to be embedded elsewhere. If you fork it, read the macOS signing block first, because it enables the hardened runtime while turning off notarization in the target configuration and the disk image block, and an afterSign hook pointing at a notarization script sits alongside both, so the path that actually runs on your machine is worth confirming before you ship anything to anyone else.

## FAQ

### How do I add a task to todometer from another program?

Three routes exist. A custom URL scheme registered by the app accepts todometer://add?text=... links, a separate browser clipper repository adds tasks from a page with one click, and a local REST API plus MCP server can be enabled from the settings drawer for scripts and assistants.

### Does the todometer Windows installer need administrator rights?

No. The NSIS configuration sets perMachine to false, so the installer defaults to a per-user location, and oneClick is false so the user gets an install wizard rather than a silent install. The installation directory can also be changed during setup.

### Which platforms does todometer ship builds for?

The build produces a disk image and a zip on macOS, an NSIS installer on Windows and a single AppImage on Linux, with a combined pack target for all three. The packaged binaries are published on the GitHub releases page.

### What does the daily reset do in todometer?

The list resets automatically each day, with optional reset notifications and a configurable reminder frequency. Reset and copy buttons can be shown or hidden from the display options, and the database can be moved from the settings drawer.

### How is the todometer MCP server distributed?

Its entry module lives in the source tree and is copied into the packaged application as an extra resource under a shorter path, so it exists as a file on disk that an agent configuration can point at rather than being compiled into the main process.

## Sources

- [cassidoo/todometer on GitHub](https://github.com/cassidoo/todometer)
- [License: MIT](https://github.com/cassidoo/todometer/blob/main/LICENSE)
- [Project website](https://cassidoo.github.io/todometer/)
- [README](https://github.com/cassidoo/todometer/blob/main/README.md)
- [Releases](https://github.com/cassidoo/todometer/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/cassidoo-todometer
