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

@nickmeriano/task

v0.7.1

Published

A task manager that lives in your repo — plain-text tickets in .task/, a zero-dependency CLI for humans and agents, and a live kanban/table UI via `task serve`.

Readme

@nickmeriano/task

A task manager that lives in your repo.

Tasks and their status are coupled to the code — so keep them next to it. State is plain text in .task/ at your project root — one markdown file per ticket and per comment: no accounts, no connectors, no drift between the board and the branch. Ticket changes show up as readable diffs in PRs, and two branches editing different tickets merge cleanly. Built for the way projects work now — you and your AI agents share one backlog, and the board updates live while agents move tickets from another terminal.

npx @nickmeriano/task init

init creates .task/ — commit it, that's the point.

Teach your agent

The package ships an agent skill at skill/SKILL.md with the conventions that make a shared board work: check it before starting, move tasks as work actually progresses, comment at milestones, file what you discover instead of letting it evaporate.

npm i -D @nickmeriano/task    # pnpm add -D / yarn add -D all work
npx skills add ./node_modules/@nickmeriano/task/skill

A local path rather than a URL, on purpose: you get the skill that your installed version ships, it works offline, and nothing is fetched from a third party. skills is one manager among several — any other works the same way, pointed at that directory.

It deliberately isn't installed for you. Where skills live differs by agent and by repo layout, and a task manager guessing at that puts a copy somewhere nothing reads — which is exactly what happened the first time this tried.

Use

task add "Wire up webhook retries" --tags api --milestone launch
task list                      # open tasks, board order
task list --needs-human        # what's blocked on a person
task start 1                   # → in_progress
task done 1                    # → done
task link 2 --blocked-by 1     # dependency, visible from both tasks
task update 1 --pr https://github.com/you/repo/pull/42
task comment 1 "shipped in #42" --author claude
task boards                    # every board in the repo, with prefixes
task archive --all             # move finished tickets to .task/archive/
task serve                     # opens the kanban + table UI, live
task publish                   # same board, at a URL you can open on a phone

Claiming — so two workers never pick up the same ticket

When agents (or agents and you) work one backlog, picking up a ticket needs a lock. task claim is that lock, built out of things git already guarantees:

task claim TAS-21            # claim it: branch + status flip + push
task list --claimable        # the queue: what a worker may pick up next
task claim --release TAS-21  # abandon a claim cleanly
  • The claim is the work branch. task claim TAS-21 branches task/claim/tas-21 off origin's default branch, flips the ticket to in_progress as the branch's first commit, and pushes. The branch existing on origin is the claim — nothing side-band to clean up, because the branch was needed anyway and dies at merge.
  • The namespace is yours. task/claim/ is only the default — "claimPrefix" in .task/config.json moves the whole namespace, and it lives in the committed config on purpose: every worker and every clone must agree on what "claimed" looks like, or there is no lock. If your workers are Claude Code cloud sessions, set "claimPrefix": "claude/task/" — that runtime can push claude/-prefixed branches without extra ceremony. Pick the value before the first claim; renaming later strands in-flight claims.
  • Atomic by construction. The push only succeeds if the branch doesn't exist yet (a compare-and-swap on the ref), so two concurrent claimers of the same ticket get exactly one winner. Exit codes are the contract: 0 claimed, 1 already claimed (pick the next ticket), 2 not claimable — not todo, blocked, --needs-human, or a dirty working tree.
  • The lifecycle rides the branch. Claim = in_progress; before the PR is marked ready, task done + task update --pr on the branch; the merge lands code and done together, and the branch auto-deletes. Between claim and merge the default branch still says todo — the branch is the truth about in-flight work, and git ls-remote origin 'task/claim/*' (or your configured namespace) lists all of it.
  • Claim before you work — humans too. The same verb covers manual work: run task claim <id> before starting a ticket yourself and any scheduled agent will skip it. task list --claimable shows todo tickets in position order minus blocked, needs-human, and already-claimed — the top entry is what an autonomous worker takes next, which makes column order your priority queue.

From anywhere

task serve is localhost, which is the right answer right up until you're not at that machine. task publish gives the same board a URL:

task publish            # private — only people who can see the repo on GitHub
task publish --public   # anyone with the link
task unpublish          # take the URL down again

It opens a browser once to install a GitHub App on that repository, and prints task.nickmeriano.com/<owner>/<repo>. After that it's just a URL — commit and push, and the board updates, including when an agent in a cloud session is the one committing.

  • Read-only, by construction. The App asks GitHub for Contents: Read-only, so the hosted board cannot write to your repo even if it wanted to. Editing still happens where it always did: the CLI, an agent, or task serve.
  • Private by default — including for public repos. Your code being open source doesn't mean you meant to publish a live feed of what you're working on next. --public is opt-in, and re-running with the other flag flips it.
  • Nothing is copied. Tasks are read from .task/ on your default branch, per request. The URL is a window onto your repo, not a second database — and task publish writes nothing to .task/, so there is no state to commit and no token in your repo.
  • No new account. You sign in with GitHub, and who can see the board is whoever can see the repository — there are no seats, invites or member settings, because there's no permission model separate from GitHub's.
  • Reversible. task unpublish removes the board and the URL 404s immediately; your tasks are untouched, because they were only ever read from .task/. Changing your mind about visibility doesn't need that at all — task publish --private is enough.
  • Any branch. The board renders your default branch, but ?ref=<branch> renders any other — and the header grows a branch switcher when there's more than one. That's PR review for board changes: see the board as the PR would leave it, before merging. Locally none of this is needed — task serve shows your working tree, so previewing a branch is git checkout.

Since the preview URL is predictable, a small workflow in your repo can comment it on every PR that touches board state — the App itself stays read-only and never writes to your repository:

# .github/workflows/board-preview.yml
name: Board preview
on:
  pull_request:
    paths: [".task/**", "**/.task/**"]
permissions:
  pull-requests: write
jobs:
  comment:
    if: github.event.pull_request.head.repo.full_name == github.repository
    runs-on: ubuntu-latest
    steps:
      - uses: actions/github-script@v7
        with:
          script: |
            const branch = context.payload.pull_request.head.ref
            const url = `https://task.nickmeriano.com/${context.repo.owner}/${context.repo.repo}?ref=${encodeURIComponent(branch)}`
            const marker = "<!-- board-preview -->"
            const body = `${marker}\n📋 [Preview the board at \`${branch}\`](${url})`
            const comments = await github.paginate(github.rest.issues.listComments, { ...context.repo, issue_number: context.issue.number })
            const existing = comments.find((c) => c.body?.startsWith(marker))
            if (existing) await github.rest.issues.updateComment({ ...context.repo, comment_id: existing.id, body })
            else await github.rest.issues.createComment({ ...context.repo, issue_number: context.issue.number, body })
  • Ids are PREFIX-12 or just 12; the prefix is the first three letters of the project name ("phone" → PHO-1), or whatever task init --prefix says.
  • Ids route by prefix: a bare 12 means the nearest board, while TAS-12 reaches the TAS board from anywhere in the monorepo. An unknown or ambiguous prefix fails loudly instead of guessing. task boards lists every board — prefix, name, path, open count — with a * on the one commands target from the current directory.
  • task archive <id> moves a done/canceled ticket's directory to .task/archive/ — off the board and out of every hot path, so boards stay fast as history accumulates. Archived tickets stay readable (task show, task list --archived), keep their comments, and their numbers stay reserved forever. task archive --all sweeps everything finished; task unarchive <id> puts one back.
  • Statuses: backlog todo in_progress done canceled.
  • A task carries tags, at most one milestone, and a --needs-human flag for work an agent can't finish alone. --tag a,b matches either tag.
  • task link A --blocked-by B marks a dependency. It's one relation seen from both ends — B's page says it blocks A — and the board badges A as blocked until B is done or canceled.
  • --pr <url> attaches a pull request to a task (--prs replaces the list); attached PRs show in the task's rail and as a count on its card. The elegant loop is an agent convention, not a webhook: put the id in the PR title ([TAS-6] …) and run task update TAS-6 --pr <url> when opening the PR — the shipped skill tells agents to do exactly that.
  • Every task has Copy link — the icon next to its title, or right-click / long-press any card or row — so handing a ticket to an agent is one paste.
  • Comments are attributed from git config user.name with no setup — override with --author or $TASK_AUTHOR, and check with task whoami.
  • Every command takes --json — that's the agent interface.
  • Commands work from any subdirectory; the CLI walks up to find .task/ the way git finds .git.
  • task serve opens your browser on the board (--no-open doesn't) and takes the next free port when 4400 is busy, so a server you left running elsewhere can't stop you — --strict-port fails on a taken port instead.
  • task publish works out the repo from your git remotes; pass --repo owner/name if the guess is wrong (a fork with two remotes, say).
  • task serve is the one command that also looks down: run it at a monorepo root and it serves every nested board from one server, with a board switcher in the header. Run it inside a package and you get just that board. Every other command stays scoped to the nearest .task/.
  • Boards from before 0.6 stored their state as a committed SQLite database; those keep working as-is, and task migrate moves one onto text files (the database stays on disk as an ignored backup).

How it's built

  • Zero dependencies. Storage is markdown files with a tiny frontmatter block — reviewable in a PR, mergeable by git, editable by hand. Comments are one file each (append-only merges cleanly), and the blocks/blocked-by relation is stored on one side only, so its two views can't disagree. Legacy boards read through Node's built-in node:sqlite (Node ≥ 22.13) — nothing to compile, so npx starts in about a second.
  • Realtime is a file watch. task serve is one node:http process: the prebuilt UI, a JSON API, and a server-sent-events stream that pings whenever anything writes to .task/ — this UI, another terminal, an agent mid-run.
  • The UI is deliberately simple. Five fixed statuses, a kanban board with drag and drop, a table, a detail panel. Built on the mojo design system.

Docs: task.nickmeriano.com