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

@postlyra/cli

v0.5.0

Published

Mine content ideas from your own git history — your commits never leave your machine

Readme

postlyra

Mine content ideas out of your own git history, on your own machine. Your commits never leave it — only the finished ideas do.

$ postlyra mine
myrepo: 14 commit(s), 6 already mined, 8 new
website: 3 commit(s), 0 already mined, 3 new

Pushed 6 idea(s) — 6 new, 0 updated:
  - the queue timeout bug that cost a night of sleep
  - why I killed the Sunday cron in favor of a Friday button
  - a before/after on the onboarding flow's time-to-first-post
  ...

What it does

postlyra mine reads the git repo you run it in, pulls the commit subjects from the last N days, and asks an AI (yours — see below) to turn that work log into a short list of finished posts: each idea is one insight, written ready to publish, with a short working title, which pillar and format it fits, and — for reels — exactly what to screen-record. The finished ideas get pushed to your Postlyra account, where you pick the platform and adjust length; the raw commits never leave your machine.

Point it at a folder of Markdown notes too (vault in the config) and the notes you capture as you work are mined the same way: every .md note touched inside the window, subfolders included, plus every bullet in content-inbox.md at the top of that folder, the file you dump one-liners into. Commits tell it what you built; notes tell it what you were thinking.

Where it mines

postlyra mine works on the folder you run it in, the way git does:

cd ~/Sites/myproject && postlyra mine   # this repo (from any subfolder too)
cd ~/Sites && postlyra mine             # every repo directly inside ~/Sites
cd ~/notes && postlyra mine             # your notes, when ~/notes is your vault
postlyra mine --all                     # every configured folder, plus your notes

Shipped something? Run postlyra mine in that repo and you get ideas about that work, not about everything else you touched this week.

A folder with no repo in or around it stops with a hint instead of quietly mining everything. Your notes join a plain run only when you ask (--vault), or when you run it inside the notes folder itself. --all is the sweep: every folder in folders plus the vault, from anywhere.

The privacy pitch

Postlyra can't read your local git clones, and you shouldn't have to hand a SaaS access to your repos just so it can read commit subjects. This CLI flips that: it runs entirely on your machine.

  • Your commits are read locally by git log, right here on your disk.
  • They're sent only to the AI you already run and pay for — the claude CLI if it's on your PATH, or the Anthropic API directly if you set ANTHROPIC_API_KEY. Either way, that's a call you control, to a provider you already trust with your code.
  • Only the finished ideas reach Postlyra — a short working title, the finished post text, a pillar, a format, maybe a recording note. No commit messages, no diffs, no file contents, no repo names beyond the short label you configured (e.g. myrepo, not the machine path it lives at).

If neither claude nor ANTHROPIC_API_KEY is available, the tool refuses to run rather than silently doing something else with your commit text.

Install

npm install -g @postlyra/cli
postlyra --help

Or run it without installing anything globally:

npx @postlyra/cli --help

Zero runtime dependencies — nothing else to install. Requires Node.js 20 or newer.

Setup

postlyra init     # API URL, folders for --all, optional notes folder, mining window
postlyra login    # opens the browser to approve -> stores a token, picks a brand

login works like gh auth login: it opens Postlyra in your browser, where you are already signed in (two-factor and passkeys included), you approve once, and the CLI receives an API token on a local callback. No password is ever typed into the terminal, and the token shows up on Profile → API tokens named after this machine — revoke it there any time.

On a machine with no browser to open (a container, an SSH session), run postlyra login --with-password for the email/password/two-factor prompts instead.

init and login write to ~/.config/postlyra/config.json, chmod 600 because it holds your API token.

One login token serves every brand (workspace) on your account. login pins this machine to one brand — automatically when the account has only one, by asking when it has several. Switch any time with postlyra brands use "Channel One", or override a single run with --brand "Channel One".

Brands are picked by name — case and punctuation don't matter, so channel-one and channelone find "Channel One" too. Every brand also has an id (shown by postlyra brands, and on Settings → Brand in Studio); it's only needed when two brands on the account share a name, which the CLI refuses to resolve rather than guess at.

Usage

postlyra login                 Sign in through the browser, store an API token, and pick a brand
postlyra init                  Interactive setup (API URL, folders for --all, notes folder, mining window)
postlyra brands                List the account's brands (`*` marks the selected one)
postlyra brands use <name>     Pick the brand this machine mines into
postlyra mine [options]        Mine the repo you are in (or the repos in this folder) into content ideas
postlyra status                Check auth, brand, folders, and unmined-commit count
postlyra upgrade               Upgrade the CLI to the latest published version
postlyra --version             Print the installed CLI version

Mine options:

| Flag | Meaning | | ------------ | ------------------------------------------------------------------- | | --all | Mine every configured folder plus your notes folder, from anywhere | | --vault [P] | Also mine your notes folder: the configured one, or path P | | --no-vault | Leave the notes folder out (with --all, or when run inside it) | | --brand B | Mine into this brand for one run (name or id), overriding the configured brand | | --days N | Override the configured mining window, in days | | --max N | Override the configured maximum ideas for this run | | --dry-run | Collect + filter only — print the work log, skip the AI call and the push | | --json | Machine-readable output instead of the friendly terminal summary |

Environment variables:

| Variable | Meaning | | -------------------- | ---------------------------------------------------------------- | | POSTLYRA_API_URL | Overrides the configured API URL for one invocation | | ANTHROPIC_API_KEY | Fallback AI backend when the claude CLI isn't on PATH |

Config reference

~/.config/postlyra/config.json:

{
  "apiUrl": "https://api.postlyra.com",
  "token": "...",
  "brand": "0f8fad5b-d9cb-469f-a165-70867728950e",
  "brandName": "My Brand",
  "folders": ["~/Sites/myproject", "~/code"],
  "exclude": ["*/website"],
  "vault": "~/Sites/second-brain",
  "days": 7,
  "maxIdeas": 8,
  "allBranches": true
}
  • apiUrl — your Postlyra API base URL. Defaults to https://api.postlyra.com, so postlyra login works out of the box with zero config. POSTLYRA_API_URL overrides this per-invocation (also what the test suite uses to point at a stub server instead of the real API).
  • token — the Sanctum API token from postlyra login. Never commit or share this file.
  • brand — the brand (workspace) this machine mines into: the brand's UUID id from postlyra brands, sent as the X-Postlyra-Brand header on every mining call. The id is stored rather than the name you typed, so renaming the brand in Studio doesn't strand this config. Empty is fine on a single-brand account (the server uses your only brand); a multi-brand account must pick one or every mine run refuses to guess. brandName is display-only.
  • folders: the paths postlyra mine --all scans (a plain mine needs none: it works on the folder it runs in). Each entry is either a git repo itself (has a .git directory) or a parent folder holding several repos one level down (e.g. ~/Sites containing ~/Sites/myproject, ~/Sites/website, ...). ~ expands to your home directory. A repo found directly under a parent folder is labeled parent/repo (Sites/myproject); a folder that's a repo itself is labeled by its own name. A plain mine gives a repo the same label, so its refs match whichever way you mine it; a repo no configured folder covers is labeled parent/repo.
  • exclude — glob-ish patterns matched against the label, e.g. "*/website" excludes every product's website repo without touching anything else. * matches within one path segment only.
  • vault: optional path to a folder of Markdown notes (an Obsidian vault, or any folder of .md files). Two things are mined from it, both entirely on your machine: every .md note modified inside the window, subfolders included (hidden folders like .obsidian and node_modules are skipped), and every bullet in content-inbox.md at the top of the folder. If the folder has a 0-inbox/ subfolder, only that subfolder is mined: the layout versions before 0.5.0 required. --all mines it; a plain mine only with --vault, or when run inside it.
  • days — default mining window; --days N overrides it for one run. It bounds vault notes by modification time as well as commits by date.
  • maxIdeas — how many ideas one run asks the AI for, and the hard cap on what gets pushed; --max N overrides it for one run. Defaults to 8. This was a server-side setting until 2026-08-15 (Postlyra's Settings → Content page); it lives here now, with everything else that decides how a run is scoped.
  • allBranches — when true, git log --all is used so commits on branches other than the checked-out one are included too.

How the dedup works

Idea text comes out of an AI, so the same commits through two runs (or two models) can produce different headlines — text can never be the identity for "have I already mined this." Identity lives on the source instead:

  1. Every source item gets a stable ref: <label>@<short-sha> for a commit (e.g. myrepo@a1b2c3d), vault:<path> for a note (its path inside the notes folder, so just the filename for a note at the top), and vault:content-inbox.md@<hash> for one bullet of the inbox file — the inbox is a list you append to forever, so the file can't be the unit. Reformatting a bullet keeps its ref; rewording it mines again, which is what you want: the idea changed.
  2. Before calling the AI, postlyra mine fetches GET /ideas/mining-context, which returns every ref already attached to an existing idea (a 90-day window server-side). Commits whose ref is already known are dropped from the work log before the AI ever sees them — this is the primary dedup, it works across machines, and it saves tokens.
  3. Every idea the AI proposes is asked to cite the refs (in refs) of the commits it drew from; the CLI validates those against what it actually collected and falls back to attributing the whole relevant section if the model's citation is missing or invalid. Those refs get pushed alongside the idea as source_refs.
  4. Server-side, POST /ideas/import also computes an idempotency key from the sorted refs + headline, so re-running mine with an overlapping window (or after a failed push) updates the same idea instead of duplicating it.

Run postlyra mine --dry-run --json any time to see exactly what would be collected, what got filtered as already-mined, and what the AI would see — without spending a single token or pushing anything.