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

@guinetik/cardstock-cli

v0.7.0

Published

Command-line companion for Cardstock

Readme

Cardstock CLI

Command-line companion for Cardstock. Initialize tracker configuration, validate Markdown offline, preview changes, and sync Markdown with the board using explicit conflict resolution and recovery. Apply requires a server deployed with sync protocol 5 and its database migrations.

Requires Node.js 22 or newer. Bun is only needed by package developers.

Features in 0.7.0

Sync learns a board's tags and lanes instead of rejecting them: a tag or lane the board already declares gets written back into cardstock.json on the next sync, so the tracker's scheme catches up to the board without a manual edit. A card downloaded from the board keeps the tracker's own spelling for its tags, not the board's.

A single invalid card no longer holds up the whole board: sync skips that card and applies the rest, and both status and sync --dry-run name it under skipped before any write happens. --dry-run validates the same content a real sync would write, not just the tracker as it sits on disk, so a preview catches what a write would actually do.

Previous releases

Version 0.6.0 adds single-card sync: cardstock sync --card 19 (or status --card 19) reads, validates, uploads/downloads and checkpoints only that card, leaving unrelated changes and conflicts pending; JSON reports card: "19" and scopes clean/remaining to it. Recovery backups moved under .cardstock/ instead of beside Markdown files. Deploy migration 20260921000000_cli_single_card_sync.sql for per-card snapshots; older protocol-5 servers still work but return full board snapshots.

Version 0.5.0 adds database-managed checklist items using ## Checklist in Markdown. Checklist items sync independently of the card body; concurrent checklist changes require --ours <id>:checklist or --theirs <id>:checklist. A missing section preserves remote items on first contact; removing a tracked section clears the list.

Version 0.4.0 adds explicit deletion and requires sync protocol 4. Version 0.3.0 does not support deletion and uses sync protocol 3.

  • Portable cardstock.json configuration and offline Markdown validation.
  • Board-allocated card IDs with cardstock new, counting reserved deleted IDs.
  • Browser-approved sign-in; no database credentials or app checkout required.
  • Read-only status and dry-run plans with field-level uploads, downloads and conflicts.
  • Bidirectional sync using a saved baseline, explicit ours/theirs choices, transactional uploads, revision/identity checks and resumable recovery.
  • Free-form areas, board-defined epics and tags, explicit audience classification, and preservation of untouched formatting and unknown nested frontmatter.
  • Explicit single/bulk card deletion, retained deletion snapshots, recoverable local backups, and delete-versus-edit conflicts across checkouts.

This release requires sync protocol 5 for writes. Upgrade the server and database before upgrading a tracker client; older apply protocols are refused, not silently downgraded. See Generic fields and audience and Interrupted sync and recovery.

Install and connect

npm install -g @guinetik/[email protected]
cardstock --version
mkdir tracker
cardstock init --project acme --board product --dir tracker --remote https://cardstock.example.com
cardstock login
cardstock validate --json
cardstock sync --dry-run --json
# After reviewing the plan:
cardstock sync --json
cardstock status --json

Use existing project/board slugs from the website. init creates local configuration, not a hosted project or board. An empty tracker downloads remote cards on its first sync. If files already exist on both sides, resolve any first-contact differences before applying; never delete a baseline to force a result.

Or run without a global installation (pin the same version for repeatable use):

npx --yes @guinetik/[email protected] --version

Licensed under GPL-3.0-only; see LICENSE.

Agent quick reference

Use the installed cardstock command from the tracker repository. In the Cardstock source repository only, bun run cli <command> runs the development source instead. Read the existing configuration before changing it; --config <path> selects a specific tracker and --remote <url> overrides its configured server.

| Command | Effect | | --- | --- | | validate --json | Offline checks; no writes. | | new "<title>" --json | GET the board for the next free ID and write <id>.md; no board writes. | | status --json or sync --dry-run --json | GET-only plan; no tracker/baseline changes. | | baseline --json | Save agreed state locally; no board writes. | | sync --json | Apply both directions and save verified baseline checkpoints. | | sync --card 19 --dry-run --json | Preview only #19; no writes. | | sync --card 19 --json | Sync only #19, preserving other cards and their baselines. | | status --card 19 --json | Check only #19; clean describes that card. | | sync --ours 19:body --json | Choose local content for that conflicting field only. | | sync --theirs 19:frontmatter.priority --json | Choose the board value for that conflicting field only. | | delete 19 --dry-run --json | Preview explicit deletion of only #19; no writes. | | delete --file deletions.txt --json | Delete only the listed IDs; retain local backups and remote snapshots. | | sync --resume --json | Continue the recorded operation, preserving its retry ID. | | sync --abort --json | Archive an interrupted intent; does not undo completed writes. | | login --no-browser | Print a browser approval URL for the user. | | logout | Revoke the token and remove the local credential. |

A normal task-update workflow:

cardstock status --json
# If the reviewed plan has safe pending changes, sync before editing.
cardstock sync --json
# Edit the appropriate tracker/<id>.md; preserve unrelated edits.
cardstock validate --json
cardstock sync --dry-run --json
# Apply only after reviewing the new plan:
cardstock sync --json
cardstock status --json
git diff -- tracker

Only write to the board when the user's task authorizes it. For a read-only review, stop after status/dry-run. Do not select a conflict winner merely to obtain a clean result: inspect the baseline/local/remote values and choose per field, or ask the user. ours is local Markdown; theirs is the board, never an ownership flag.

For automation, read JSON from stdout and keep stderr separate. Preview counts are under counts; a completed apply reports pending work under remaining and remote write IDs under applied (including deletions/restorations; downloads are not listed there). Exit 0 means success, not necessarily a clean board. Require ok: true, clean: true, and no diagnostics when checking completion. Exit 1 signals conflicts/validation failures; exit 2 signals configuration, authentication, filesystem, network or execution failure. Errors can occur after a remote commit: inspect the journal before retrying.

An already-synced tracker checks one snapshot and returns without creating a journal or rewriting its baseline (baseline.saved: false). When cards change, only cards needing publication or a refreshed baseline enter the recovery journal.

For one-card work, add --card <id> to the status, preview and sync commands above. It supports local-only cards, remote-only cards and deletion tombstones. A missing ID is an error; a missing local file still never requests deletion. Conflict choices must refer to that card. Resume uses the saved selection automatically; --card cannot be combined with --resume, --abort, baseline or delete.

Keep .cardstock/, *.md.cardstock-*.before and *.md.cardstock-*.tmp out of Git. Never commit credentials or print tokens, never infer deletion from a missing card file, and never overwrite a corrupt baseline or live lock. Recovery is described below. A downloaded board edit changes local files: review those changes and commit them only when the task includes committing work.

Configuration and validation

Minimal cardstock.json (paths are relative to this file):

{
  "version": 1,
  "project": "acme",
  "board": "product",
  "tracker": "tracker",
  "remote": "https://cardstock.example.com"
}

init creates cardstock.json in the current directory and refuses to replace it. validate searches this directory and its parents for that file. --config <file> selects it explicitly. Tracker paths resolve relative to the configuration file. The tracker directory must already contain <id>.md files; other filenames are ignored, matching the existing ETL. No issue files are changed during validation.

Validation uses the website's frontmatter contract and checks IDs against filenames. An optional scheme adds board-specific workflow, tag vocabulary and cardinality, open-item summary and required-section checks. Scheme validation also checks strict YAML syntax (including duplicate keys); minimal configurations retain the existing lenient parser. JSON diagnostics include a code and field, plus a scheme document reference for board rules. Exit codes: 0 success, 1 invalid issues, 2 command/configuration/filesystem error.

Import an existing tracker configuration

cardstock init --from backlog/board.json --out cardstock.migrated.json --dry-run --json
cardstock init --from backlog/board.json --out cardstock.migrated.json
cardstock validate --config cardstock.migrated.json

Omit --out to create cardstock.json when it does not already exist. Both normal creation and preview refuse an existing destination. Preview writes no files. After reviewing and validating an alternative output, back up the existing configuration and adopt the new file as cardstock.json, or keep selecting it with validate --config. --from cannot be combined with --project, --board or --dir; board identity comes from the legacy file.

The importer embeds the old scheme and mapping in the version-1 configuration. It resolves tracker, mapping and seed paths relative to board.json. For legacy repository-relative scheme_doc, it searches from that directory through its ancestors and reports the first existing document it finds. Tracker, scheme document and seed references are then saved relative to the new configuration. Keep that relative directory layout when relocating the tracker. The referenced files and tracker directory must exist at import time; validation subsequently only needs the new configuration and Markdown files.

Missing files, unknown keys and unsupported rules fail before configuration is written. $comment metadata is preserved. provisioning.seed records the SQL seed for administrators and recovery; the CLI never executes it. Credentials remain outside the configuration. --remote <url> is optional for offline work.

Configuration import preserves legacy mapping metadata and reports changed semantics. Group aliases resolve tag spelling; legacy audience rules are never evaluated. init and offline validate do not contact the board to verify tags or lanes; sync and status do, as described under Apply and conflict choices. Migrating clients must pass Cardstock's contract, not reproduce their old project's validator. Keep provisioning artifacts until the new workflow is verified.

To recover from an incorrect configuration, select the saved original file or correct the imported one and rerun validation. Init and validation do not modify issue files or the remote board, and do not create a sync baseline.

Sign in

cardstock login opens the Cardstock website for approval. Sign in with your existing Cardstock account, approve the request, then return to the terminal. The credential is a personal access token saved in your user configuration directory, outside the tracker and repository. cardstock logout revokes that token remotely and removes the local credential.

Pass --remote or configure a remote URL with cardstock init. Use --no-browser to print the approval URL without opening it.

File a new card

cardstock new "<title>" allocates the next card ID and writes <id>.md in the configured tracker directory.

cardstock new "Cards do not show how old they are" --tags bug,card
cardstock new "Board is slow to load" --summary "It takes ages to appear." --json

The ID comes from the board, not from the folder. The snapshot lists live cards and the IDs of deleted ones, which stay reserved, and local files are counted too, so an unsynced card cannot be handed out twice. This is why the command requires a configured remote and a signed-in session: an ID picked offline can collide with a card someone created on the site. It is still not a reservation against concurrent creation — preview before uploading, as in Preview and baseline.

The file is written and nothing else. No card is created on the board and no baseline is touched; run validate, then sync, once the ## Ask says what was actually asked.

| Flag | Default | |---|---| | --summary <text> | the title, because an open item needs a nonempty summary | | --status <status> | backlog | | --lane <lane> | unsorted | | --epic <name> | Board & cards; --epic "" writes no epic | | --area <name> | UI | | --tags <tag,tag> | enhancement | | --effort H\|M\|L, --priority 1\|2\|3 | omitted |

A new card never starts at a gate: the status and lane defaults keep filing separate from promotion. The result is checked against cardstock.json's scheme before anything is written, so an unknown tag, a status its lane cannot hold, or a missing required section fails with the same diagnostics validate would print, and no file is created.

Preview and baseline

status and sync --dry-run produce the same plan. Both support --config, --remote, and --json, use the credential saved by login, and make only GET requests to the board API. They write no tracker files, configuration or baseline state. An empty tracker can preview cards to download; no missing file is treated as an instruction to delete a remote card.

The planner compares each frontmatter field and the body against a saved baseline. It reports local-only edits as uploads, remote-only edits as downloads, identical edits on both sides as equal changes, and different edits to one field as conflicts. One card may have both upload and download fields. JSON includes each field's baseline, local and remote values and whether the field is present; removing a field differs from setting it to null. Counts are per card and directions can overlap. Unknown nested fields are compared, tags/relations compare as sets, and tag aliases and unambiguous bare tags use the board vocabulary. Formatting-only frontmatter changes, CRLF versus LF and the leading title H1 do not create edits. To avoid ambiguous comparisons, preview requires valid YAML even when a minimal configuration permits lenient parsing during validate.

Without a baseline, a local-only card is a proposed upload and a remote-only card is a proposed download. Differing existing cards require reconciliation; the CLI does not infer which side changed. Different titles under the same ID are flagged as a possible identity collision. A previously tracked card disappearing remotely without a retained deletion record is a conflict, never an automatic recreation or deletion. A confirmed deletion downloads only when the local copy is unchanged; local edits require an explicit delete-versus-edit choice. Duplicate IDs and filename/ID mismatches fail planning.

Once both sides agree and validation passes, explicitly record their agreed state:

cardstock baseline --remote https://cardstock.example.com

This writes only .cardstock/<scope-hash>.json beside the configuration. The scope includes remote, project, board, relative tracker path and mapping, keeping state separate for each checkout and board. Ignore .cardstock/ in version control; baseline files contain card Markdown. They contain no credentials. Equal edits since a previous baseline can be recorded with the same command. status and sync --dry-run never advance it.

Baseline writes use an exclusive lock and atomic replacement. Corrupt, mismatched or concurrently changed state fails without resetting it. If a run is interrupted, inspect the reported baseline path and make sure no CLI process is using it before removing its stale .lock/.tmp artifacts. Preserve the last valid JSON file; discarding it loses the information needed to classify earlier edits. Recreating a missing baseline still requires agreement on both sides.

Preview exit codes: 0 for a successfully computed plan without conflicts or validation errors (pending uploads/downloads can still exist); 1 for conflicts, scheme validation errors, or a refused baseline; 2 for configuration, credentials, filesystem, malformed input/snapshot, or network errors. clean means there are no pending data changes or conflicts; check ok/diagnostics as well before using a plan. Snapshot and tracker changes during a read require retrying the preview.

Apply and conflict choices

cardstock sync --remote https://cardstock.example.com --json
cardstock sync --dry-run --ours 17:body --theirs 18:frontmatter.priority
cardstock sync --ours 17:body --theirs 18:frontmatter.priority

Ours always means local Markdown; theirs always means the hosted board. A choice affects only the selected conflicting fields, preserving unrelated edits from both sides. Omit :field to select all conflicts on a card. Repeat flags for additional cards. Missing/replaced identities cannot be resolved by choosing a content side. Legacy app-owned/file-owned body flags never choose the winner in this sync path.

The CLI records the complete intent before writes. Remote uploads are a single transaction containing the card, tags, links and history. Existing rows require immutable UUIDs and revisions; new rows use insert-only creation. An operation ID makes retries idempotent, including when the server committed but its response was lost. Snapshots are read back before local publication and baseline advancement. Each verified card checkpoints independently; a failure never advances unverified cards. Later edits can still appear as pending work in the final preview.

Older baselines lack immutable IDs. Once both sides agree, rerun baseline against the updated server. Alternatively, sync --adopt-identities explicitly trusts the currently observed identity mapping for this one upgrade. It does not override a known UUID mismatch or resolve any content conflicts.

Optional field removal is a real removal, not an instruction to retain an old database value. Untouched source formatting and nested unknown YAML are preserved. Rank is a position in a lane: invalid or contradictory positions fail the entire remote batch. Ambiguous tied lane ranks require a board reorder before applying. Nonempty tag/epic/area tag-derivation overrides remain unsupported: put the desired tags directly in frontmatter before removing those rules. Normal tag references, unambiguous bare tags and group aliases are supported. No alias name is built in.

sync and status read the board's own tag groups and lanes and validate against them, so a tag or lane added on the board never blocks the run that learns it. sync then writes whatever the configuration lacked into cardstock.json, appending to the arrays those entries belong in and leaving $comment, key order and formatting alone, and reports it under learned. A card that fails validation is skipped instead of holding the batch: the other cards apply, a skipped card stays pending for the next run, and each one is named with its diagnostics under skipped. Any skip keeps ok: false, so a partial apply cannot be read as a clean one. sync --dry-run and status report both fields as what a real sync would do, and write nothing themselves.

Explicit card deletion

Deletion requires the same signed-in board-admin access as other sync writes. It never needs database credentials, SQL seeds or a missing-file heuristic.

cardstock delete 17 --dry-run --json
# Review the exact card and action before applying:
cardstock delete 17 --json

# Bulk: one positive integer card ID per line; blank lines and # comments allowed.
cardstock delete --file deletions.txt --dry-run --json
cardstock delete --file deletions.txt --json

You can also pass several IDs: cardstock delete 17 18. Choose positional IDs or --file, not both. Duplicate/invalid IDs and empty lists are errors; at most 1,000 IDs may be selected. The command affects only those cards, not unrelated pending edits. --config, --remote and --json work as for sync. The final remaining counts cover the whole tracker, so a successful scoped deletion may not be clean.

Deleting a live card requires a saved baseline with its immutable UUID. First sync or establish agreement with baseline; new local-only cards must be synced before this command can delete them. Removing a Markdown file by itself still causes a download, never a remote delete. Completed tasks should normally be marked done or archived, not deleted. Agents must have user authorization for the selected IDs.

Each deleted identity stays reserved remotely with its last sheet snapshot and a new revision. Ordinary sync carries that deletion to other checkouts. Unchanged local copies move to .cardstock/<scope>.json.backups/<operation>/17.md.before; locally edited copies produce an existence conflict. The JSON plan reports delete_remote, delete_local, or restore_remote explicitly; existence values are booleans (false means deleted). Never interpret an absent API card as a tombstone.

| Conflict | --ours <id> | --theirs <id> | | --- | --- | --- | | delete 17: hosted card changed since baseline | Delete the hosted card; preserve local bytes in a backup. | Cancel deletion and download the hosted content. | | sync: hosted deletion versus local content | Restore the local content remotely under the same UUID. | Accept deletion and move the local file to a backup. |

cardstock delete 17 --ours 17 --dry-run --json
cardstock delete 17 --ours 17 --json

# In another checkout with a local edit that should survive a hosted deletion:
cardstock sync --ours 17 --dry-run --json
cardstock sync --ours 17 --json

Existence conflicts are whole-card decisions; content-field flags cannot resolve them. A replaced UUID is still an identity error, not an ours/theirs choice. To restore after accepting a deletion, copy the preserved .before file back to 17.md, inspect status, then explicitly choose sync --ours 17. Simply restoring a file never silently recreates the hosted card. Restoration validates current lanes/tags and restores sheet content and outgoing links, not deleted comments, event history or incoming links. Keep administrator backups when full undo matters.

The remote batch is atomic and revision-checked. Repeated requests use the same operation receipt; interrupted deletion uses sync --resume, not a new delete command. sync --abort archives intent without undoing an already committed deletion. Keep journal archives and .before files until results are verified.

Administrator deletions after the migration also create tombstones. Deletions before it was installed cannot be reconstructed and remain missing-identity conflicts. Tombstones remain until explicit restoration or deletion of the parent board; there is no automatic expiry, CLI purge or reuse of deleted IDs.

Deployment: apply migrations through 20260919000000_card_checklist.sql and run etl/backfill-checklist.ts --apply, deploy the protocol-5 server, then upgrade the CLI. Complete pending operations with their matching client/server before upgrading. This release refuses older servers for writes; the new server refuses protocol-3 writes. Provisioning remains administrator-only: the CLI does not execute provisioning.seed or add a --seed command.

Generic fields and audience

area: Customer experience
epic: Improve onboarding
audience: internal
tags: [enhancement]

Area is free-form nonempty text, not an enum. Epic is an optional assignment by name (up to 200 characters): sync reuses an epic on that board or creates it. Clear the assignment with an omitted, empty or null epic; this never deletes the epic or its other cards. Epics created on the site can be assigned to cards and round-trip normally. Empty epics without cards have no standalone Markdown file. Legacy scheme.areas and scheme.epics lists are retained as suggestions only, and a legacy required-epic rule does not prevent an unassigned card.

Audience is an independent filter classification: all (the UI's General option) or internal. It is not an access-control boundary. A board needs no internal tag; a tag named internal or any area/epic name has no automatic effect on audience. Legacy audience_internal_when is retained as inert metadata, never evaluated by sync or the importer. Existing database audience values are not migrated or reset.

Omitted audience means all. Protocol-3 snapshots expose the existing stored classification, so old baselines download an internal value before future edits. Removing a previously synced audience: internal is an explicit change back to all. Field merging, revision checks and recovery apply as for other fields. Older clients/servers cannot apply across this protocol change. Finish or archive pending journals before upgrading; do not delete the baseline or recovery files.

Interrupted sync and recovery

cardstock sync --resume --remote https://cardstock.example.com
cardstock sync --resume --recover-lock --remote https://cardstock.example.com
cardstock sync --abort --remote https://cardstock.example.com

Resume uses the saved intent and retry ID, not a freshly guessed winner. Do not pass new --ours/--theirs choices to resume. If either side changed incompatibly, preserve those edits, abort the old intent, then preview/reconcile a fresh plan. Abort archives the journal; it does not roll back completed remote or local writes. An explicit baseline replacement is refused while a journal is pending.

The scope lock excludes other cooperating sync/baseline writers. --recover-lock only reclaims a lock recorded on this machine whose process has exited. Unknown owners and live processes require inspection, never automatic lock stealing.

Local files are staged and flushed, then published with an exclusive filesystem link so a file that appears concurrently is never overwritten. An existing file is first moved to its per-operation .before name and checked again. New backups are stored at .cardstock/<scope>.json.backups/<operation>/<id>.md.before, beside the baseline and journal. A crash in that brief filename gap is recoverable with --resume; file contents are never published partially. If tracker and configuration are on different filesystems, backups stay beside the Markdown files so the move remains atomic. Displaced originals are retained to preserve writes from editors holding an old file descriptor. These backups and journal archives contain Markdown, not credentials. Journals from older releases still recover backups beside the original files; existing backups are not moved or deleted. Finish new operations with CLI 0.6.0 or newer. Add these patterns to each tracker's ignore file:

.cardstock/
*.md.cardstock-*.before
*.md.cardstock-*.tmp

Do not delete recovery artifacts until the run is verified and any concurrent editor changes have been reconciled. Hard-link publication requires a filesystem that supports hard links; an unsupported filesystem stops with originals retained. File data is flushed; directory durability follows platform filesystem guarantees (Windows does not expose POSIX directory fsync through Node).

.before files are intentional recovery copies, not sync inputs. They remain after success because an editor can still hold and write the displaced original. There is no automatic expiry. Once the operation is verified and editors have closed or reopened the affected files, obsolete backups can be removed manually.

Apply exits 0 when the recorded operation completes without remaining conflicts, 1 for plan/validation conflicts, and 2 for failed or interrupted execution. JSON errors include the pending journal path and recovery guidance when available.