npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@zerodawn/td

v0.5.0

Published

Minimal todo capture + view with @tags and per-window filter

Readme

@zerodawn/td

Minimal todo CLI with @tags and per-tmux-window filter.

Install

npm install -g @zerodawn/td

For zsh completion:

mkdir -p ~/.local/share/zsh/completions
ln -sf "$(npm root -g)/@zerodawn/td/_td" ~/.local/share/zsh/completions/_td

First run

On first use, td asks where your todos should live. It stores that location and reuses it later.

No default suggestion is imposed; you choose the path yourself.

Filter model

Default filter is all. Filters are per-tmux-window.

  • plain td ls / td view shows everything in all
  • td filter @tag focuses one tag
  • td filter untagged shows only items without tags
  • td filter clear returns to all

Active / inactive (per list)

Within a list filter (@work, @private, @backlog, untagged, or any @tag), items split into two ordered sets:

  • active — your working set; what you see by default
  • inactive — voorraad; only shown on request

A list keeps independent active/inactive state, so the same item with @work @backlog can be active in @work and inactive in @backlog.

Until you reorder or activate within a list, all matching items render as active (legacy behaviour). The first td activate/deactivate/up/... in a list materializes a per-list entry in todos.order.json. From then on, plain captures (td, td w, td p, td b) land in inactive of that list; use the wa/pa/ba/a shortcuts to capture-and-activate in one step.

In all, active/inactive does not apply.

Commands

# capture
td "text @tags"             capture new item (lands in inactive once the list has state)
td w text                   capture with @work
td p text                   capture with @private
td b text                   capture with @backlog
td wa text                  capture with @work and activate
td pa text                  capture with @private and activate
td ba text                  capture with @backlog and activate
td a text                   capture untagged and activate

# view
td ls                       list active items in current filter
td ls active                same, explicit
td ls inactive              list inactive items in current filter
td ls --all                 active, blank line, inactive (continuous numbering)
td view                     sidebar-format render (active only)

# state (current list filter; not allowed in 'all')
td activate <id|nr>         move to bottom of active
td deactivate <id|nr>       move to top of inactive
td a <nr>                   shorthand for 'activate <nr>' (numeric-only arg)

# reorder within active (current list filter; not allowed in 'all')
td up <id|nr>               1 step up
td down <id|nr>             1 step down
td top <id|nr>              to top of active
td bottom <id|nr>           to bottom of active
td mv <id|nr> <pos>         to 1-based position in active

# edit / remove
td rm <id|nr>               delete item by timestamp prefix or list number
td untag <id|nr> <tag>      remove one tag from an item

# filter
td filter @tag              set filter for current tmux window
td filter                   show current window filter
td filter clear             reset current window filter to all
td filter all               show all items
td filter untagged          show only untagged items

# location
td location                 show current todo location
td location set /path       set todo location and create it if needed
td location migrate /path   move todos.md (and sidecar) to a new location

td help                     show this summary

Numeric positions in reorder/state commands refer to the combined ls --all view (active first, then inactive). Reorder commands (up, down, top, bottom, mv) error if the target is not in active.

Examples

td location set /data/notes/todos
td "review pipeline"           # untagged, inactive after first activate
td "fix worktree @pi @urgent"
td wa urgent prod bug          # @work + active
td w boring chore              # @work, inactive

td filter @work
td ls                          # urgent prod bug
td ls --all
#  1  14:03  urgent prod bug @work
#
#  2  14:05  boring chore @work

td activate 2                  # promote boring chore to active
td up 2                        # reorder within active
td deactivate 1                # demote urgent prod bug to inactive

td rm 1                        # remove by list number (cleans sidecar too)
td untag 1 @work               # also drops id from @work sidecar

td filter clear
td location migrate /data/archive/todos   # moves todos.md + todos.order.json

Per-window filter

td filter ... writes to /tmp/td/window-filters/<window-id>. Ephemeral — resets on reboot (intentional; windows are also ephemeral).

td view and td ls automatically read the filter for the current window. The tmux-sidebar script calls td view from within the sidebar pane, which is in the same window as the active context.

File format

One item per line in todos.md:

- 2026-05-20T13:42:17  fix gitflow worktree @pi @urgent
- 2026-05-20T14:01:55  review pipeline

Safe to edit in vim. Do not change the - TIMESTAMP prefix format or td rm will not find the item.

Ordering and active/inactive state live in a sidecar todos.order.json next to todos.md:

{
  "@work": {
    "active": ["2026-05-20T14:03:11"],
    "inactive": ["2026-05-20T14:05:02"]
  },
  "@private": { "active": [], "inactive": [] },
  "untagged": { "active": [], "inactive": [] }
}

Safe to delete: removing the sidecar reverts every list to legacy "all-as-active in capture-order". Stale ids referring to removed items are ignored on render and pruned on next write.

Removing items and tags

td rm and td untag accept either:

  • a list number from the current filter view
  • a full timestamp
  • a unique timestamp prefix

Numeric positions are filter-aware: in a list filter (@work, @private, @backlog, untagged, or any @tag), the number refers to the ls --all view of that filter (active first, then inactive). In all, the number refers to capture-order across todos.md.

Examples:

td filter @work
td ls --all
#  1  14:01  fix auth
#  2  14:03  ship release
#
#  3  14:05  refactor
td rm 3                  # removes "refactor" (3rd in @work view)
td untag 1 @work         # drops @work tag from "fix auth"

td filter all
td rm 2026-05-20T14:05   # by timestamp prefix — works in any filter

Config

Saved in:

  • $TD_CONFIG_FILE, or
  • $XDG_CONFIG_HOME/td/config.json, or
  • ~/.config/td/config.json

Example:

{
  "todosDir": "/data/notes/todos"
}

Local development

npm install
npm run smoke
npm publish --dry-run

Publishing

npm login
npm publish --access public

Known limitations

  • IDs are second-based ISO timestamps. If multiple captures happen in the same second, td waits until the next second to keep IDs unique.
  • td location migrate refuses to overwrite a non-empty target todos.md.