Skip to content
Eugene Lazutkin edited this page Jul 12, 2026 · 1 revision

ghmeta

Sync package.json metadata with the GitHub repo. description maps to the repo About line, keywords map to repo topics. Push is the default direction (package.json → GitHub); pull copies the other way. Thin bash wrapper over gh + jq.

Usage

ghmeta            # push: package.json → GitHub About/topics (default)
ghmeta push -n    # show planned GitHub changes without applying
ghmeta pull       # GitHub About/topics → package.json
ghmeta pull -n    # show planned package.json changes without writing

The repo resolves from the current git remote (gh repo view); package.json from the git toplevel — run it from anywhere inside a repo.

Commands

Command Description
push, p Copy package.json description/keywords → GitHub About/topics (default)
pull Copy GitHub About/topics → package.json description/keywords

Options

Option Description
-n, --dry-run Show planned changes — and the exact gh calls (push) or jq rewrite (pull) — without applying anything

Semantics

  • Topics are replaced as a whole set on push (true sync): topics added by hand on GitHub that aren't in keywords drop. gh repo edit --add-topic is the additive alternative if that's ever wanted; replacement is the point here.
  • Normalization on push: keywords are lowercased and whitespace runs become hyphens; anything still invalid as a GitHub topic (charset a-z0-9-, must start alphanumeric, ≤ 50 chars) is dropped with a warning. GitHub caps topics at 20 — the excess is dropped with a warning naming the casualties, so the API call never fails on count.
  • Missing fields are skipped, not synced-to-empty: push without a description/keywords field in package.json leaves the corresponding GitHub side untouched (with a warning); pull with an empty About / no topics likewise leaves package.json untouched. A present but different value syncs.
  • Per-field idempotence: fields already in sync are reported and skipped; a fully-in-sync repo is a no-op exiting 0.
  • Pull writes atomically and mode-preserving: jq output lands in a same-directory tempfile, chmoded to the original mode, then mved over package.json. npm-style 2-space formatting survives byte-identically.
  • After a real push, the final GitHub state is read back and displayed for verification.

Fleet usage

Preview every repo in one loop:

for d in ~/Open/*/; do (cd "$d" && [ -f package.json ] && gh repo view &>/dev/null && ghmeta -n); done

Drop -n once the previews look right.

Dependencies

gh (authenticated) and jq, checked at startup via deps::need.

Clone this wiki locally