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

dotmd-cli

v0.87.0

Published

CLI for managing markdown documents with YAML frontmatter — index, query, validate, graph, export, lifecycle, and AI summaries.

Readme

runlist

CLI for managing Markdown documents with YAML frontmatter.

runlist (formerly dotmd) indexes, queries, validates, graphs, exports, and lifecycle-manages plans, ADRs, RFCs, design docs, and other structured Markdown. It is built for AI-assisted development workflows where documents need to remain current and safe to mutate.

  • Zero runtime dependencies
  • Node.js 20 or newer
  • Runtime support for Linux, macOS, and Windows
  • Type-aware lifecycle rules for plans, docs, and saved prompts

Install

npm install -g dotmd-cli    # global CLI and Claude Code plugin hooks
npm install -D dotmd-cli    # project scripts via node_modules/.bin
npx dotmd-cli init          # try it without installing

runlist is the canonical executable, rl is its short convenience alias, and dotmd remains supported during the compatibility window. All three invoke the same CLI. The package is still published as dotmd-cli, and the Claude Code plugin is still dotmd@dotmd; those names change in a later release. Legacy dotmd.config.* files, DOTMD_* variables and .dotmd/ state keep working.

Maintainer release automation is POSIX-only because it uses Bash and POSIX command-line tools. The published Node.js CLI remains cross-platform.

Agent host setup

The CLI alone gives an agent no orientation and no session identity. Install the integration for whichever host you run:

runlist install             # what's installed for each host
runlist install claude      # Claude Code plugin (marketplace + plugin)
runlist install opencode    # OpenCode plugin (one auto-discovered file)
runlist doctor --session    # what identity runlist sees here, and from where

Both are one-time and global; runlist update keeps them in step with the CLI. Codex needs no install for identity: it exports CODEX_THREAD_ID to every tool shell, and runlist reads it as a per-session identity automatically.

Claude Code Plugin

runlist install claude runs the two steps below for you. From inside a session:

/plugin marketplace add reowens/dotmd
/plugin install dotmd@dotmd

The plugin provides SessionStart and SubagentStart orientation, a UserPromptSubmit hint that gives the exact runlist baton form when you ask for a handoff, a PreToolUse guard, the canonical workflow skill, and /plans, /docs, /prompts, and /baton commands.

OpenCode Plugin

runlist install opencode writes one plugin file into OpenCode's global config directory, where OpenCode auto-discovers it — no opencode.json edit. It supplies the two things the CLI cannot get on its own:

  • Per-session plan ownership. OpenCode exports no session id to a tool shell. Without the plugin, runlist falls back to OPENCODE_PID, which names the OpenCode process — so every session in one OpenCode instance shares an identity and can release the others' in-session plans.
  • A session-start briefing, the equivalent of Claude Code's SessionStart hook. OpenCode's Claude Code compatibility covers skills and the system prompt, not hooks, so nothing else runs runlist hud.

Restart OpenCode after installing. The file is version-stamped (runlist-generated:, or dotmd-generated: from older releases); a dotmd.js without either stamp is treated as hand-authored and is never overwritten.

The plugin requires a global CLI install because its hooks resolve runlist (or dotmd) from PATH. A project devDependency is useful for npm scripts but does not put the CLI on the hook's PATH.

Keep the CLI and plugin aligned with:

runlist update
runlist update --check
runlist update --cli-only
runlist update --plugin-only

Restart Claude Code, or run /reload-plugins, after a plugin update.

Quick Start

runlist init                    # create config, docs/, and the generated index
runlist new plan auth-refresh  # scaffold a typed document
runlist briefing               # compact active-work orientation
runlist plans                  # live plan dashboard
runlist check                  # validate schema, references, and lifecycle shape
runlist doctor                 # preview repairs; add --apply to write

runlist briefing is the compact orientation view. runlist context is the fuller human/LLM briefing, while runlist agent-context emits bounded structured JSON for agent integrations.

Core Workflow

runlist briefing
runlist use docs/plans/auth-refresh.md
runlist set awaiting docs/plans/auth-refresh.md --note "Need API owner decision"
runlist set active docs/plans/auth-refresh.md --note "Decision received"
runlist archive docs/plans/auth-refresh.md --note "Shipped and verified"

Use runlist set <status> [<file>] for lifecycle changes rather than editing a status: line. It validates the status for the document type, updates history, runs lifecycle hooks, repairs references after moves, and synchronizes the index.

For unfinished session work, save the handoff and release the owned plan in one operation:

runlist baton @/tmp/resume.md

Baton refuses when a handoff for the same work is already pending, so one piece of work never has two resume prompts.

Saved prompts are local session state. Consume them with runlist use; inspect without consuming via runlist prompts show. Consuming a baton prompt also claims its plan; runlist use --no-claim reads and archives it without starting the plan.

New plans are created planned; runlist use starts one, and runlist new plan <name> --status <status> sets a different starting status.

Document Format

---
type: plan
status: active
updated: 2026-07-13
modules:
  - auth
surfaces:
  - backend
current_state: Token validation is complete.
next_step: Wire refresh rotation into middleware.
related_docs:
  - ./auth-design.md
---

# Auth Refresh

- [x] Validate tokens
- [ ] Rotate refresh tokens

status is the only universally required field. A type enables type-specific statuses, validation, templates, and briefing behavior. Explicit frontmatter wins, but runlist can also derive titles, summaries, state, next steps, checklist progress, and Markdown links from the body.

Use plural modules: and surfaces: arrays. The old singular keys remain readable for compatibility and can be migrated with runlist lint --fix.

Built-In Types

| Type | Purpose | Default statuses | |---|---|---| | plan | Executable work | in-session, active, planned, blocked, partial, paused, awaiting, queued-after, archived | | doc | Specs, ADRs, audits, and reference material | draft, active, review, reference, deprecated, archived | | prompt | Saved future-session instructions | pending, archived |

Status definitions can be customized per type. Rich status objects co-locate display, staleness, validation, terminal, and archive behavior in one place.

Runlists And Roadmaps

A sprint runlist is an ordered runlist: array on a hub plan. Scaffold a hub and children together:

runlist new plan auth-revamp --runlist extract,rewrite,cleanup
runlist runlist auth-revamp
runlist runlist next auth-revamp

Mutate the structure through the CLI so the array, child parent_plan refs, and body order list remain synchronized:

runlist runlist add auth-revamp docs/plans/existing-plan.md
runlist runlist add auth-revamp follow-up
runlist runlist reorder auth-revamp follow-up --before cleanup
runlist runlist remove auth-revamp extract --clear-parent

Archived children count as complete. Parked children (blocked, partial, paused, awaiting, and queued-after) are skipped when choosing the next pickup but do not count as done.

For a larger prose-first domain map, create a coordination runlist:

runlist new plan platform-work --coordination
runlist runlists

runlist new hub platform-work makes the same coordination hub; add --runlist a,b,c or --roadmap for the other two shapes.

For progress across several runlists, create a roadmap:

runlist new plan platform-roadmap --roadmap
runlist roadmap platform-roadmap
runlist roadmap platform-roadmap next

Roadmaps roll up progress recursively and choose the first startable plan across their child runlists. Runlists and roadmaps are held out of actionable plan counts so dashboards do not double-count their children.

Decisions

A decision is an entry in its plan, not a document of its own. Write the record (the situation, what exists today, what each answer leaves in place) to a file and add it:

runlist new decision auth-revamp --question "Which token store?" @record.md

It takes the next id (D1, D2, …) that no decision item in that plan uses, lands at the end of the plan's top-level decisions section (created before ## Version History when there is none) with a Disposition: OPEN. line, and refuses an empty record. --disposition held parks it instead.

A corpus that indexes its decisions in one register numbers them in one sequence. Name the register, and the id follows the highest the register or the plan uses, and the register gets the entry's row in the same locked write:

export const decisions = {
  section: 'Decisions',
  prefix: 'D',
  register: { file: 'docs/plans/register.md', statusLine: 'waiting on you:' },
};

The register block is the fenced block whose first line carries statusLine. The row is the question plus --answers (what each answer leaves in place), which is required when a register is configured.

Flags

A flag is something someone found that the person should know about when they come back: a plan that contradicts another, a decision open in one place and ruled in another, a citation that no longer says what it claims. Any session, person or check can add one, and no model is needed:

runlist flag add docs/plans/auth.md:42 "says tokens expire in 1h; the spec says 24h" --severity problem
runlist flags                 # open flags, problems first, newest first
runlist flag accept F3 --note "real, owner agrees"
runlist flag reject F4        # not a problem; closed
runlist flag resolve F3       # fixed
runlist check --flag          # the check's errors become flags, attributed to it

Each flag keeps the text of the line it points at, so the list says when that line has moved or changed since. A repeat of an open flag on the same place is merged. The log is append-only, .runlist/flags.jsonl by default (export const flags = { file } moves it), and triage is recorded as events beside the flag, never over it. Every session start shows a count and the top open flags.

Safety Model

  • Mutation commands support --dry-run / -n.
  • Managed writes are confined to configured document roots.
  • Lifecycle and multi-file moves use atomic, conflict-aware mutation paths.
  • Session ownership is durable local state, not inferred from telemetry.
  • Passive orientation commands do not mutate repository state.
  • Repository paths in machine and human output use stable slash-normalized identities across supported operating systems.

Command Reference

The CLI is the source of truth for command syntax and options:

runlist --help
runlist help all
runlist help statuses
runlist <command> --help

Shell completion is generated from the same command registry:

eval "$(runlist completions bash)"
eval "$(runlist completions zsh)"

This README intentionally documents onboarding and concepts instead of duplicating the complete command catalog.

Configuration

Run runlist init to create runlist.config.mjs (a legacy dotmd.config.mjs is still read). A minimal typed configuration:

export const root = 'docs';
export const archiveDir = 'archived';

export const types = {
  plan: {
    statuses: {
      'in-session': { context: 'expanded', staleDays: 1 },
      active: { context: 'expanded', staleDays: 14 },
      planned: { context: 'listed', staleDays: 30 },
      archived: {
        context: 'counted',
        archive: true,
        terminal: true,
        skipStale: true,
        skipWarnings: true,
      },
    },
  },
};

Code references

Archive and rename repair every reference inside the doc roots. codeRoots extends that to source files that cite a document by its repo-relative path in a comment, a docstring or a string literal:

export const codeRoots = ['packages', 'scripts', 'services'];
export const codeRefsUntouched = ['scripts/guards/plan-baseline.json'];

runlist refs <old> <new> reports every citation by file and line and writes only on --fix; runlist refs repair does the same for every archived document still cited at its previous path. Archive and rename print the count afterwards and take --fix-refs. Only the full repo-relative path matches, never a bare basename, and only that segment is replaced, so a trailing § or # anchor survives. A citation inside a string literal waits for --strings, a file in codeRefsUntouched is never written, and with no codeRoots set nothing is scanned.

Configuration supports multiple roots, custom types and templates, taxonomy, reference fields, presets, rendering, lifecycle hooks, validation hooks, and AI summarization hooks. See runlist.config.example.mjs for the complete annotated reference.

Hooks

Functions exported from runlist.config.mjs are detected as hooks. They can add validation, customize rendering and summaries, or react to lifecycle events. Hooks receive the resolved config and command context; mutation hooks participate in the command's dry-run and failure contracts.

License

MIT