@postlyra/cli
v0.4.0
Published
Mine content ideas from your own git history — your commits never leave your machine
Maintainers
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 walks the git repos you point it at, 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 notes vault too (vault in the config, or --vault PATH)
and the notes you capture as you work are mined the same way: everything
in <vault>/0-inbox/ touched inside the window, plus every bullet in
<vault>/0-inbox/content-inbox.md, the file you dump one-liners into.
Commits tell it what you built; notes tell it what you were thinking.
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
claudeCLI if it's on your PATH, or the Anthropic API directly if you setANTHROPIC_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 --helpOr run it without installing anything globally:
npx @postlyra/cli --helpZero runtime dependencies — nothing else to install. Requires Node.js 20 or newer.
Setup
postlyra init # API URL, folders to scan, optional vault, mining window
postlyra login # opens the browser to approve -> stores a token, picks a brandlogin 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, vault, 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 recent commits and vault notes 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 versionMine options:
| Flag | Meaning |
| ------------ | ------------------------------------------------------------------- |
| --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 |
| --vault P | Mine this vault path for this run, ignoring the configured one |
| --no-vault | Skip the vault entirely — commits only |
| --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 tohttps://api.postlyra.com, sopostlyra loginworks out of the box with zero config.POSTLYRA_API_URLoverrides 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 frompostlyra login. Never commit or share this file.brand— the brand (workspace) this machine mines into: the brand's UUID id frompostlyra brands, sent as theX-Postlyra-Brandheader 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 everyminerun refuses to guess.brandNameis display-only.folders— paths to scan. Each entry is either a git repo itself (has a.gitdirectory) or a parent folder holding several repos one level down (e.g.~/Sitescontaining~/Sites/myproject,~/Sites/website, ...).~expands to your home directory. A repo found directly under a parent folder is labeledparent/repo(Sites/myproject); a folder that's a repo itself is labeled by its own name.exclude— glob-ish patterns matched against the label, e.g."*/website"excludes every product'swebsiterepo without touching anything else.*matches within one path segment only.vault— optional path to a notes vault (Obsidian or any folder of markdown). Two things are mined from it, both entirely on your machine: every0-inbox/*.mdnote modified inside the window, and every bullet in0-inbox/content-inbox.md. Leave it empty (or pass--no-vault) to mine commits only. A vault with no0-inbox/directory is skipped with a warning rather than an error.days— default mining window;--days Noverrides 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 Noverrides 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 --allis 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:
- Every source item gets a stable ref:
<label>@<short-sha>for a commit (e.g.myrepo@a1b2c3d),vault:<filename>for a note, andvault: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. - Before calling the AI,
postlyra minefetchesGET /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. - 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 assource_refs. - Server-side,
POST /ideas/importalso computes an idempotency key from the sorted refs + headline, so re-runningminewith 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.
