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

hyper-pm

v0.1.6

Published

Git-backed work-item CLI with optional GitHub sync

Downloads

1,007

Readme

hyper-pm

Git-native project management CLI: an orphan data branch, disposable temp worktrees, append-only JSONL event log, and optional GitHub Issues sync.

Requirements

  • Node.js 20+
  • Git repository with hyper-pm initialized (init)
  • Optional: GitHub auth for sync (GITHUB_TOKEN or authenticated GitHub CLI via gh auth login) and repo slug; HYPER_PM_AI_API_KEY for AI ticket helpers

Install and run

From the monorepo root:

pnpm build --filter=hyper-pm

Then either:

  • Direct: node apps/hyper-pm/dist/main.cjs <command> [options]
  • On PATH: link the package or use pnpm exec hyper-pm from a workspace that depends on hyper-pm
  • Discover flags: hyper-pm --help, hyper-pm epic --help, hyper-pm epic create --help, etc.

Global options

These apply before any subcommand (for example hyper-pm --format text epic read).

| Option | Description | Default | | ---------------------------- | ------------------------------------------------------------ | --------------------------------- | | --format <fmt> | Output format: json or text | json | | --temp-dir <dir> | Parent directory for disposable git worktrees | TMPDIR / system temp | | --keep-worktree | Do not remove the temp worktree after the command | off | | --repo <path> | Git repository root (defaults to current working directory) | cwd | | --data-branch <name> | Data branch name (overrides config / init) | from config, else hyper-pm-data | | --remote <name> | Remote name (init / config) | origin | | --sync <mode> | Persisted sync mode: off, outbound, or full | (see init / config) | | --github-repo <owner/repo> | GitHub repository slug | env / config | | --actor <label> | JSONL actor for CLI mutations (overrides HYPER_PM_ACTOR) | resolved from git user / env |

Commands

Work item display numbers

Each epic, story, and ticket has a durable ULID id plus a per-type number: a small positive integer (separate counters for epics, stories, and tickets). List JSON includes number on each row; full read JSON includes it on the record.

Any CLI flag that accepts a work item --id (or a parent like --epic, --story, or ticket list filters that take ids) also accepts that item’s number when it is written as an all-digit token (for example ticket read 3 after you confirm ticket 3 in ticket read list output). Resolution tries the ULID/id string first, then a unique numeric match. If two items share the same number after a bad merge, numeric lookup is ambiguous and the command fails until the JSONL conflict is fixed.

Parallel offline edits can still assign the same next number on different branches; resolving that is the same class of problem as other JSONL merge conflicts.

init

Create or adopt the orphan data branch and write .hyper-pm/config.json.

Uses global options only (no subcommand-specific flags).

epic

| Subcommand | Description | Options | | ---------- | --------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | create | Create an epic | Required: --title <t>. Optional: --body <b> (default ""), --id <id>, --status <s> (backlog, todo, in_progress, done, cancelled; default backlog) | | read | One epic by id, or list all if --id omitted | --id <id> | | update | Patch an epic | Required: --id <id>. Optional: --title <t>, --body <b>, --status <s> (same values as create) | | delete | Soft-delete an epic | Required: --id <id> |

story

| Subcommand | Description | Options | | ---------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | | create | Create a story under an epic | Required: --title <t>, --epic <id>. Optional: --body <b> (default ""), --id <id>, --status <s> (default backlog) | | read | One story or list all | --id <id>. Optional when listing: --epic <id> (only stories under that epic) | | update | Patch a story | Required: --id <id>. Optional: --title <t>, --body <b>, --status <s> (same status values as epic) | | delete | Soft-delete a story | Required: --id <id> |

ticket

| Subcommand | Description | Options | | --------------- | ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | create | Create a ticket | Required: --title <t>. Optional: --story <id> (omit to create an unlinked ticket; story must exist), --body <b> (default ""), --id <id>, --status <s> (default todo), --assignee <login> (GitHub login, normalized), --branch <name> (repeatable; link git branches), --label <name> (repeatable), --priority <p> (low, medium, high, urgent), --size <s> (xs, s, m, l, xl), --estimate <n> (non-negative number), --start-at / --target-finish-at (ISO-8601), --ai-draft (draft body via AI; needs HYPER_PM_AI_API_KEY) | | read | One ticket or list all | --id <id> (JSON includes storyIdnull when unlinked — plus assignee, linkedBranches, comments (from ticket comment), prActivityRecent, and planning fields labels, priority, size, estimate, startWorkAt, targetFinishAt when set / list rows omit empty planning fields). When listing (omit --id): --story <id> or --epic <id> or --without-story (each mutually exclusive); --status <s> repeatable (OR of backlog, todo, in_progress, done, cancelled); --priority <p> / --size <s> repeatable (OR); --label <name> repeatable (ticket must have every listed label); --estimate-min / --estimate-max; --start-after / --start-before, --target-finish-after / --target-finish-before (ISO-8601, inclusive, on startWorkAt / targetFinishAt); date bounds --created-after / --created-before, --updated-after / --updated-before, --status-changed-after / --status-changed-before (ISO-8601, inclusive); --created-by / --updated-by / --status-changed-by (substring, case-sensitive); --title-contains (case-insensitive); --github-linked (only tickets with githubIssueNumber); --branch <name> (normalized exact match on a linked branch); --sort-by (id, title, status, storyId, createdAt, updatedAt, statusChangedAt, assignee, githubIssueNumber, lastPrActivityAt, priority, size, estimate, startWorkAt, targetFinishAt; default id); --sort-dir asc|desc (default asc; ties break on id ascending) | | update | Patch a ticket | Required: --id <id>. Optional: --title <t>, --body <b>, --status <s> (same status values), --story <id> (attach to story; mutually exclusive with --unlink-story), --unlink-story (clear story), --assignee <login>, --unassign (mutually exclusive with --assignee), --add-branch <name> / --remove-branch <name> (repeatable; mutually exclusive with --clear-branches), --clear-branches (clear all linked branches), --add-label <name> / --remove-label <name> (repeatable; mutually exclusive with --clear-labels), --clear-labels, --priority <p> (mutually exclusive with --clear-priority), --clear-priority, --size <s> (mutually exclusive with --clear-size), --clear-size, --estimate <n> (mutually exclusive with --clear-estimate), --clear-estimate, --start-at / --clear-start-at, --target-finish-at / --clear-target-finish-at, --ai-improve (rewrite --body with AI; requires --body and HYPER_PM_AI_API_KEY) | | work | Start work in the primary clone | Required: --id <id>. Optional: --branch <name> (default hyper-pm/<id>; if that local branch already exists, appends -2, -3, … to the whole preferred name, e.g. hyper-pm/ticket-1hyper-pm/ticket-1-2), --from <ref> (fork point; default: refs/remotes/<remote>/HEAD when present, else main, master, or HEAD). Creates and checks out the new branch, sets the ticket to in_progress, and appends the branch to linked branches. Rejects tickets in done or cancelled. Success JSON includes branch and, when a suffix was applied, branchPreferred. | | comment | Append a ticket comment (local event log) | Required: --id <id>, --body <text> (non-empty after trim). Appends TicketCommentAdded; ticket read --id includes a comments array. | | delete | Soft-delete a ticket | Required: --id <id> | | import-github | Import GitHub issues as local tickets | Optional: --dry-run (list candidates and skips, no JSONL writes), --story <id> (attach every imported ticket to this story; must exist), --state <s> (open, closed, or all; default all — passed to GitHub’s issues list), --issue <n> (repeatable or comma-separated; only consider those numbers). Requires issueMapping: "ticket" in config. Skips pull requests, issues already linked to a ticket, and issues whose body contains a non-empty hyper_pm_id in the JSON fence (live ticket, deleted ticket, or broken reference). Maps title (strips [hyper-pm] prefix), description before the first fenced block, closed state → done, first assignee, non-reserved labels, and known planning fields from the fence. After a real import, run sync --with-github once so outbound sync rewrites each GitHub body with the hyper-pm fence. |

Tickets store at most one assignee (GitHub login). When you run sync --with-github, outbound updates set that user as the issue’s only assignee (extra assignees on the issue may be removed), and inbound reads the first assignee from the API when the issue has several. Outbound creates GitHub issues only for tickets that have a valid epic+story chain; unlinked tickets are skipped until you set --story. Tickets that already have a linked GitHub issue are still updated when unlinked (issue body drops parent ids until you link again).

repo

Read-only helpers against the checked-out repository (not the hyper-pm data worktree).

| Subcommand | Description | | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- | | commit-authors | JSON { "items": [{ "name", "email", "loginGuess?" }] } — unique authors by email (most recent first), with optional GitHub login guess. | | suggest-assignee-login | JSON { "loginGuess": string \| null }. Required: --email <e>. Optional: --name <n> when the email alone is not enough. |

sync

Default (no flags): reconciles the hyper-pm-data branch over git: git fetch of the configured branch on remote, git merge of refs/remotes/<remote>/<dataBranch> when that ref exists (bounded push retries on likely non-fast-forward races), then git push. Does not call the GitHub Issues API and does not require GITHUB_TOKEN or gh. Runs even when sync is off in config. JSON includes gitDataOnly: true plus dataBranchFetch, dataBranchMerge, dataBranchPush, optional dataBranchPushDetail, and pushAttempts. A merge conflict aborts the merge and exits with a user error. Pass --skip-push to fetch/merge without pushing.

--with-github: additionally runs GitHub Issues sync when sync is outbound or full in config (not when sync is off). Outbound sets issue labels to hyper-pm, ticket, plus each ticket label (GitHub’s 50-character label limit applies), and embeds ticket planning in the fenced JSON after the description. Inbound (sync: full) compares the issue’s non-reserved labels and that fence to the projection and appends GithubInboundUpdate when anything differs. With sync: full, after inbound it may load linked PR timelines for in_progress tickets with Refs / Closes / Fixes #<n> in the body (GithubPrActivity). Requires GITHUB_TOKEN or gh auth login. After GitHub API work, hyper-pm commits on the data branch using git -c user.name=… -c user.email=… commit (same identity resolution as mutations). JSON then includes githubSync: true plus dataBranchPush / optional dataBranchPushDetail (same push semantics as the default path).

--skip-network: skips all sync network work (no git fetch/merge/push, no GitHub). JSON { "ok": true, "skipped": true }. The legacy spelling --no-github is still accepted (it is normalized to --skip-network before parsing).

--git-data: legacy no-op; default behavior is already git-only. If passed, JSON may include legacyGitDataFlag: true.

For any path that pushes, a missing remote or push error does not fail the command for exit code: JSON still has "ok":true with dataBranchPush set to pushed, skipped_no_remote, failed, or (with --skip-push) skipped_cli. Push failures are also written to stderr as a single-line hint.

| Option | Description | Default | | ---------------- | ------------------------------------------------------------------------ | --------------------------- | | --with-github | Also run GitHub Issues sync (needs auth; sync not off) | false | | --skip-network | Skip all sync network operations (git and GitHub); legacy: --no-github | false | | --git-data | Legacy; same as default | false | | --skip-push | Skip git push of the data branch after sync | false (push is attempted) |

For a successful push you need a configured remote and credentials that allow git push to that remote.

audit

List durable events (who / what / when) with optional filters.

| Option | Description | | ------------------ | --------------------------------------------------------------------- | | --limit <n> | Keep only the most recent n matching events | | --type <t> | Filter by event type (must be one of the values below) | | --entity-id <id> | Filter rows whose payload id, entityId, or ticketId matches | | --text-style <s> | When --format text only: tsv (default), plain, or plain-links |

Valid --type values: EpicCreated, EpicUpdated, EpicDeleted, StoryCreated, StoryUpdated, StoryDeleted, TicketCreated, TicketUpdated, TicketDeleted, TicketCommentAdded, SyncCursor, GithubInboundUpdate, GithubIssueLinked, GithubPrActivity.

GithubPrActivity payloads include ticketId, prNumber, kind (opened | updated | commented | reviewed | merged | closed | ready_for_review), sourceId, occurredAt, and optionally reviewState / url.

With --format text, --text-style selects output: tsv — tab-separated ts, type, actor, id (extra columns for GithubPrActivity); plain — one English sentence per event (timestamp: actor …); plain-links — same sentence plus a tab and compact JSON (ids and URLs; no ticket body/title text). For plain-links, if githubRepo resolves from config or GITHUB_REPO, derived issueHtmlUrl / pullHtmlUrl fields are included when applicable.

With --format json, the result includes events and any invalidLines skipped during parse; --text-style is ignored.

doctor

Validate that the event log on the data branch can be read and replayed. Exits non-zero if issues are found.

No subcommand-specific options.

Environment variables

See apps/hyper-pm/env.example. Commonly:

| Variable | Used for | | --------------------- | ------------------------------------------------------------------------------------------------------ | | GITHUB_REPO | Default repo slug (owner/repo) | | GITHUB_TOKEN | sync, ticket import-github, and GitHub identity for outbound actor (optional if gh is logged in) | | HYPER_PM_AI_API_KEY | ticket create --ai-draft, ticket update --ai-improve | | HYPER_PM_ACTOR | Default audit label for mutations (override with --actor) | | TMPDIR | Default parent for disposable worktrees (override with --temp-dir) |

Publishing to npm

The package is configured for the public registry (private unset, files whitelist, bundled CLI). Prefer pnpm publish from this app (or --filter hyper-pm) so workspace devDependencies stay correct. Run pnpm run build first so dist/main.cjs and dist/index.cjs exist.

Quickstart

pnpm build --filter=hyper-pm
node apps/hyper-pm/dist/main.cjs init
node apps/hyper-pm/dist/main.cjs epic create --title "My epic"
node apps/hyper-pm/dist/main.cjs --format text epic read