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

@obelum/cli

v0.3.0

Published

The obelum command: status, briefs, the four verbs, translation rounds and an auditor over an Obelum-tracked repository, using plain git

Readme

@obelum/cli

Part of Obelum; the repository README lists the packages and the roadmap.

The obelum command: a Node host for @obelum/core over plain git. Which languages are behind, what a translator would be told, the four verbs as commits, translation rounds with Claude, and an auditor for the synced copies.

npm install -g @obelum/cli            # or npx @obelum/cli …
npm install @obelum/translator-claude # for `obelum translate`

Setting up a repository

obelum.json at the repository root names the languages and where documents live, as path patterns with one {lang} in them:

obelum init --langs sv,no,en --anchor mdx 'src/pages/{lang}/**/*.mdx' 'content/*/{lang}/*.mdx'
git add obelum.json && git commit -m "obelum.json"
obelum mark --all          # every language synced to every other, as the files stand; one commit

A document is a pattern with {lang} left in: src/pages/{lang}/pricing.mdx names every language's file at once. Its synced copies live at .obelum/<viewer>/<real path>, one directory at the repository root. Two lines worth adding to .gitattributes:

.obelum/** linguist-generated=true
*.mdx text eol=lf

For a site translated from one language, add --source en to init ("source": "en" in obelum.json). Each target then keeps a copy of the source only. A change to the source is an edit as soon as it is committed, and makes every target stale; obelum fix on the source marks a correction as not to be translated. A change to a target needs nothing: commit it. status says which of these applies to a changed file.

anchor says which lines a brief's diff names its hunks after: mdx (a line starting with <), markdown (a heading), or git (the default, a line starting with a letter). instructions may name a file whose contents are handed to the translator verbatim, a glossary and format rules.

Commands

obelum status [--all] [--json]         which languages are behind, per document
obelum brief <file> [--json]           what a translator would be told for <file>
obelum edit <file>                     commit the working copy of <file> as an edit
obelum fix <file>                      … as a fix, merged into every sibling's copy
obelum sync <file> [--from <path>|-]   … as a sync; content from a file, stdin, or the working copy
obelum mark <file> | mark --all        mark one language, or every document, as synced
obelum translate <file> [--all]        translate <file> from its siblings with Claude [--no-ask]
obelum check [--merges N] [--json]     audit .obelum/

<file> is a real path such as src/pages/sv/pricing.mdx; the document and the language are read off it.

Reads are HEAD's, and every verb is one commit. A file edited by hand is taken in by obelum edit <file>, which commits it; before that, status does not see the change. fix and sync work the same way on the working copy, so the flow for a correction is: edit the file, obelum fix <file>. A translation produced elsewhere lands with obelum sync <file> --from translation.mdx or … --from - on stdin.

Commits go through objects, not the index. A verb writes blobs and a commit on top of HEAD and then refreshes only its own paths in the checkout, so whatever else you have staged or edited is untouched. That also means the copies need not be on disk at all:

git sparse-checkout set --no-cone '/*' '!/.obelum/'

keeps .obelum/ out of the working directory, and every command works the same, reading copies from objects and writing them as index entries with skip-worktree. Copies are written to disk only when .obelum/ is checked out; real files always are.

status

$ obelum status
src/pages/{lang}/pricing.mdx
    sv: ok   no: ok   en: stale (no)
23 documents, 1 with a language behind

Only documents with a language behind are listed; --all lists every one. A missing language shows as missing, and is not counted as behind: it is a state of the language, not staleness.

translate

$ ANTHROPIC_API_KEY=… obelum translate src/pages/en/pricing.mdx
translating src/pages/en/pricing.mdx from no…
sync src/pages/en/pricing.mdx: committed

With --all, every language of the document that is behind or missing is translated in turn, as one round: when the round closes, a language whose edit everyone has now translated is marked as synced (its copy of itself catches up; see syncAll in core). A translation that does not complete is not saved and the command exits 1.

When the documents cannot settle what to write (two languages give the same fee different amounts, say), the translator asks. In a terminal the question is put to you with its options: answer with a number or your own words, and the answer goes back to it. Enter alone is no answer. With --no-ask, with no terminal, or with no answer, the question and the translator's guess are printed, the content it is about is left as it was, and the command ends by saying how many questions were left unanswered.

check

$ obelum check
orphan: .obelum/sv/src/pages/no/old-name.mdx
    src/pages/no/old-name.mdx is not in the tree; delete the copy, or move it with the file
1 finding

Reports copies for a language not in the config, copies nothing reads (with a source, a target's copy of another target, or any copy the source keeps), copies of files that no longer exist (a rename or delete without its copies), copies with merge conflict markers, and merge commits resolved by picking sides per file, where a language's copy of a sibling came from one parent and the real sibling from the other, so the language claims a sync it never made. Exits 1 with findings.

As a library

import { run } from '@obelum/cli';
const code = await run(['status', '--json'], { cwd, stdout: console.log, stderr: console.error });

run takes the same arguments as the binary and an Io; a translator factory in the Io stands in for Claude, which is how the tests script it.

Not yet

Documents whose language is not a path segment (an unprefixed default locale at the pages root) have no pattern here yet, and translate runs over one document at a time.

License

MIT.