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

@verevoir/workflows

v0.5.0

Published

Workflow-adapter primitive: a contract over kanban/issue/objective sources (cards with status, assignee, labels) with implementations as subpath imports. Trello today; Jira, Notion DBs, Linear, GitHub Issues, aigency objectives follow.

Readme

@verevoir/workflows

Workflow-adapter primitive: one contract over kanban / issue / objective sources. Trello today; Jira, Linear, Notion databases, GitHub Issues/Projects, and an in-aigency wrapper over the objective tree follow under the same shape.

Purpose

Surfaces workflow-shaped sources (cards with status, assignee, labels, custom fields) behind a single neutral contract — so consumers write code once and swap backends. Sibling to @verevoir/sources (file-shape sources: GitHub, FS) and @verevoir/context (the cache that fronts either kind).

The contract is intentionally non-flavoured by any one tool. Column is whatever the backend calls its workflow state (Trello list, Jira status, Notion select option, aigency objective phase). Label is whatever it calls its tags. Tools' extensions land in customFields.

Most consumers reach this via MCP

If you're driving an LLM agent and want kanban / issue / objective operations as tools, you usually don't import @verevoir/workflows directly — you run the @verevoir/mcp server, which wraps the Trello adapter (and future ones) as MCP tools: list_columns / list_cards / get_card / create_card / update_card / move_card / list_comments / add_comment. See @verevoir/mcp for Claude Code config; key recommendation is "alwaysLoad": true so the tools surface as first-class instead of being deferred behind ToolSearch.

Direct in-process consumption (the usage shown below) is for: writing your own MCP server, embedding workflow operations in a non-MCP runtime, building higher-level libraries that compose multiple adapters, or implementing a new backend (Jira, Linear, Notion, custom).

Subpaths

  • @verevoir/workflows — contract module: WorkflowAdapter, Card, Column, Label, Comment, CardFilter, CardPatch, CardCreate, CustomFieldDef, CustomFieldValue, WorkflowEnv, WorkflowApiError.
  • @verevoir/workflows/trello — Trello adapter (read + write).
  • @verevoir/workflows/notion — Notion-database adapter (read + write) via @notionhq/client (optional peer dep). Maps a Notion database to the WorkflowAdapter contract: rows are cards, the auto-detected status / select property provides columns, the people property gives assignees, the multi_select property gives labels. Body content round-trips through Notion's native pages.retrieveMarkdown / pages.updateMarkdown.
  • @verevoir/workflows/obsidianObsidian Kanban plugin adapter (read + write) over a local board .md file. No credentials — boardUrl is an absolute path or file:// URL. Lanes (## headings) are columns; cards are - [ ] [[Note]] wikilinks whose linked note is the source of truth: the note's frontmatter id is the card identity, its body is Card.body, its tags are labels, and a date field is the due date. Cards without a resolvable id are skipped on reads and 404 when addressed; the board file is never mutated to assign identity. File I/O goes through @verevoir/sources (the fs adapter for local boards), and frontmatter handling uses the yaml package — both are optional peer dependencies (like @notionhq/client for Notion), required only when you use this subpath.

Install

npm install @verevoir/workflows

No required peer dependencies.

Canonical usage — Trello

import { envFromTrelloProcessEnv, trello } from '@verevoir/workflows/trello';

// Set TRELLO_API_KEY + TRELLO_API_TOKEN in your environment.
const env = envFromTrelloProcessEnv();
if (!env) throw new Error('TRELLO_API_KEY or TRELLO_API_TOKEN not set');

const boardUrl = 'https://trello.com/b/abc123/my-board';

// Read what's on the board.
const columns = await trello.listColumns(env, boardUrl);
const cards = await trello.listCards(env, boardUrl, { columnId: columns[0].id });

// Move a card across columns.
await trello.moveCard(env, boardUrl, cards[0].id, columns[1].id);

// Add a comment.
await trello.addComment(env, boardUrl, cards[0].id, 'Picked this up, starting now.');

// Create a new card.
const fresh = await trello.createCard(env, boardUrl, columns[0].id, {
  title: 'Wire pickAdapter into aigency-web',
  body: '## Acceptance\n\n- Factory at /lib/source-router.ts\n- Tests cover GH + FS dispatch\n',
  labelIds: [],
});

Usage — Obsidian Kanban

import { obsidian } from '@verevoir/workflows/obsidian';

// No credentials. Point at a local Obsidian Kanban board file.
// Set OBSIDIAN_VAULT_PATH so vault-wide `[[wikilink]]` resolution works.
const env = { token: '' };
const boardUrl = '/path/to/Vault/Board/My-Board.md';

const columns = await obsidian.listColumns(env, boardUrl); // lanes
const cards = await obsidian.listCards(env, boardUrl); // linked notes with an `id`

// Create a card — writes a new note (with a minted `id`) and links it.
const card = await obsidian.createCard(env, boardUrl, columns[0].id, {
  title: 'Draft the spec',
  body: '# Draft the spec\n\n- [ ] outline\n',
  labelIds: ['planning'],
});

// Move it across lanes (rewrites only the board file's link line).
await obsidian.moveCard(env, boardUrl, card.id, columns[1].id);

Cards are Obsidian Kanban linked notes: each - [ ] [[Note]] item resolves to a note whose frontmatter id is the card identity and whose body/tags supply the card content. Cards that are plain text or lack a resolvable id are skipped on reads. The board file is never mutated to assign identity. Assignees and comments have no Obsidian equivalent — they read empty and reject writes with WorkflowApiError(501).

The contract

interface WorkflowAdapter {
  listColumns(env, boardUrl): Promise<Column[]>;
  listCards(env, boardUrl, filter?): Promise<Card[]>;
  getCard(env, boardUrl, cardId): Promise<Card>;
  isCardFresh(env, boardUrl, cardId, version): Promise<boolean>;
  createCard(env, boardUrl, columnId, fields): Promise<Card>;
  updateCard(env, boardUrl, cardId, patch): Promise<void>;
  moveCard(env, boardUrl, cardId, toColumnId): Promise<void>;
  listComments(env, boardUrl, cardId): Promise<Comment[]>;
  addComment(env, boardUrl, cardId, body): Promise<void>;
  listCustomFields(env, boardUrl): Promise<CustomFieldDef[]>;
}

isCardFresh answers "is the version I'm holding (the lastActivity timestamp from a prior getCard / listCards) still the live one?" — the cheap freshness check cache layers (@verevoir/context's wrapWithCache) use to validate held cards without re-fetching. Returns false when the card has moved (including 404 / removed).

Card carries the universal-ish properties (title, body, columnId, parentId?, assigneeIds, labels, dueDate?, url?, lastActivity?, readableId?) plus an open customFields? bag keyed by field ID. Backend-specific fields (Jira story points, Notion select properties, etc.) land there with typed values.

readableId is the human-readable identifier when the backend has one: Trello card number, Jira issue key, Linear identifier, Notion's ID property when configured. Distinct from id (the stable record identifier the adapter uses for API calls) — readableId is what humans paste into commits, branches, PR titles.

For the Notion adapter specifically, readableId reads from the property named ID by default; override with the NOTION_READABLE_ID_PROPERTY env var. Supports unique_id (renders as <prefix>-<number> — e.g. STDIO-42), rich_text, formula.string, and title property types.

Authentication

Each adapter packs its auth shape into WorkflowEnv.token:

  • Trello"<apiKey>:<apiToken>" (split on first :).
  • Obsidian — none; the adapter reads local files only. envFromObsidianProcessEnv() returns { token: '' }.
  • Jira (future) — "<email>:<api-token>" (basic auth).
  • Notion (future) — "<integration-token>" (single value).
  • Linear (future) — "<api-key>" (single value).

Per-adapter envFromXxxProcessEnv() helpers build a valid env from process environment variables.

Hierarchy support

Cards carry an optional parentId for hierarchical workflows (Jira sub-tasks under epics, Notion nested rows, aigency sub-objectives). Trello is flat — its adapter throws WorkflowApiError(501) if a patch tries to set parentId, rather than silently ignoring.

Custom fields

Workflow tools that expose backend-defined custom fields (Jira's story points / sprint / severity, Notion DB properties, Linear estimates) populate them via the customFields map on Card, and accept writes via the customFields map on CardPatch. The schema is discoverable via listCustomFields(env, boardUrl) — consumers cross-reference returned field IDs against the values on each card.

Adapters that don't support custom fields (Trello v0) return [] from listCustomFields and leave Card.customFields undefined.

What this is NOT

  • Not a caching layer. Cache the responses via @verevoir/context (a Trello-specific subpath there is a future addition).
  • Not a sync engine. Adapters are stateless clients; cross-backend mirroring (e.g., aigency objectives ↔ a customer Jira) belongs in a separate sync layer.
  • Not opinionated about scope. boardUrl is opaque to the contract; per-adapter URL parsing defines what it means.

See also

  • @verevoir/sources — file-shape sources (GitHub, FS).
  • @verevoir/context — in-process cache for content + symbols. Fronts file sources today; will front workflow sources when read patterns warrant.
  • @verevoir/llm — provider-agnostic LLM call surface.

License

Apache-2.0.