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

@rhombus.rocks/fngit

v2.0.0

Published

Locate git repos from short references and clone them the configured way — a library plus a decorator over the git CLI.

Readme

@rhombus.rocks/fngit

A library for locating git repos from short references, plus fngit — a CLI that decorates git with that lookup.

The library resolves a user-typed reference (<name>, <name>@<owner>, <owner>/<name>, gh:<owner>/<name>, a full URL, +workspace suffixes and all) against your existing local clones and, failing that, your GitHub orgs — returning either the local path or the clone URL, optionally cloning it for you. fngit clone <ref> runs that resolution and clones the repo to its configured destination; every other git subcommand and argument passes straight through unchanged.

Install

npm install @rhombus.rocks/fngit

API

locate

import { locate } from '@rhombus.rocks/fngit';

const repo = await locate('fnclaude', { clone: true });
repo.path; // the checkout on disk

locate(input, options) resolves to a Located, or rejects with a LocateError.

type Located = { type: 'local'; path: string; ref: RepoRef; } | {
  type: 'remote';
  url: string;
  destination: string;
  ref: RepoRef;
};

local means a checkout already exists at path. remote means it doesn't: url is where it would be fetched from and destination where it would land. With clone: true a remote result is cloned first, so the call always resolves local.

ref is the parsed reference — host, owner, name, workspace, original. Its workspace is carried through untouched and never affects the destination; a +workspace suffix names a worktree beside the clone, not a different clone. When a bare name is settled by a clone on disk, owner is filled in from the owner segment the scan recovered — the one exception is a bare name matched at <dir>/<name> in an extra source root, which carries no owner in its path, so owner stays empty there.

Options

| Field | Default | Meaning | | ----------- | --------------------- | -------------------------------------------------------------------------- | | clone | false | Clone a remote result before returning, so the result is always local. | | settings | none | Per-field overlay on whatever the config file supplies. | | home | os.homedir() | Root for ~ expansion and the config-file lookup. | | gh | the real gh spawner | An IGitHubCli to call instead; inject a fake in tests. | | cloneArgs | none | Extra arguments for git clone, honoured only alongside clone. |

Resolution order

  1. Parse the reference. Accepted forms are <name>, <name>@<owner>, <owner>/<name>, gh:<owner>/<name>, an https:///http:///ssh:// URL and the git@host:owner/name form, each with an optional +workspace suffix.
  2. Load the settings chain, then apply the settings overlay field by field. A broken clone template — empty, an unknown placeholder, or a {host-short} with no alias — fails config here, before any disk scan or network call.
  3. For a bare name, before any network call:
    1. scan the clone template with {owner} wildcarded — wherever {owner} sits in it, at any depth — excluding worktree siblings, and recover each hit's owner segment onto the result; one hit resolves local, several are ambiguous;
    2. search additionalSrcDirs, each entry twice: <dir>/<name> (which carries no owner, so the result's owner stays empty), then the clone template's last segment re-rooted there;
    3. ask gh who owns it — the authenticated user first, then each organization in API order. Every candidate is probed, so a name two owners share is ambiguous rather than silently resolved.
  4. With the owner known, compute the destination from cloneTemplate. If it exists, that is the local result. Otherwise search additionalSrcDirs once more for that exact owner. Otherwise the result is remote.
  5. With clone: true, create the destination's parent and clone into it.

The clone template's root outranks additionalSrcDirs at every step, so a repo present in both is never reported ambiguous. Those extra roots are search-only and never become a clone destination.

LocateError

Every failure rejects with a LocateError carrying a structured failure, so a caller can branch on the reason rather than parse the message.

| failure.reason | Raised when | | ----------------- | --------------------------------------------------------------------------------------------------------------------- | | unparseable | The input matches none of the accepted reference forms. | | config | No cloneTemplate is configured, or expanding it failed — an unknown placeholder, or a {host-short} with no alias. | | gh-failed | The owner lookup could not reach gh at all. | | not-found | No owner reachable through gh has a repo by that name. | | ambiguous-owner | Several owners have it; owners lists them. | | ambiguous-local | Several checkouts on disk match; paths lists them. | | clone-failed | The clone itself failed; repoNotFound says whether the repo simply doesn't exist. |

CLI

npm install -g @rhombus.rocks/fngit

fngit decorates git clone: only clone is intercepted, and only when its first argument is a bare reference — every other subcommand and argument passes straight through to git unchanged.

fngit clone fnclaude          # locate() resolves the ref, cloning if needed
fngit clone fnclaude --depth 1  # extra args forward to the underlying clone

Dispatch rule

fngit clone <arg> … is decorated only when <arg>:

  • doesn't start with - (so a leading flag, e.g. fngit clone --depth 1 x, passes through — git owns its own flag parsing);
  • isn't a filesystem path (doesn't start with . or /, e.g. fngit clone ./local-path);
  • parses as one of the reference forms above; and
  • has no second positional after it (fngit clone <ref> <dir> passes through — the user chose git's destination explicitly).

Anything else — fngit clone alone, two positionals, an unparseable or path-like first argument — passes straight through to git clone. A reference carrying a +workspace suffix is rejected (exit 2); worktree support isn't implemented yet.

fngit clone somerepo ./some/path  # two positionals — the user chose the
                                   # destination, so this runs git unchanged

Use it as git

fngit is safe to shadow your real git with, so every command you type gets clone's lookup for free without changing how anything else behaves. fngit install --shadow-git (see fngit install) sets this up for you, via a git shim on PATH rather than a shell alias — do that instead of the manual steps below unless you have a reason not to.

Alias it in your shell:

# bash / zsh
alias git=fngit

# fish
alias git fngit

# PowerShell
Set-Alias git fngit

Or put a git symlink (Linux/macOS) or shim (Windows) ahead of the real git on PATH. Either way, fngit always finds and runs the real git underneath — it walks PATH itself, skipping any git that resolves back into fngit's own install, so it never recurses into itself even when it is the thing named git.

If your setup somehow still loops — git pointing at fngit, which finds git on PATH and gets back to itself — fngit detects it and refuses to run rather than recursing forever, exit 126. Set FNGIT_GIT to the real git binary's path to sidestep the lookup entirely; it always wins outright and skips the PATH walk.

Exit codes

| Code | Meaning | | ------- | -------------------------------------------------------------------------------------------------------------------------------------------- | | 0 | The clone resolved (or already existed); its path was printed to stdout. | | 1 | locate() rejected with a LocateError; its message (and, for an ambiguous result, one candidate per line) went to stderr. | | 2 | The reference carried a +workspace suffix, which fngit clone doesn't support yet; or fngit install was given an unrecognized argument. | | 126 | fngit detected it would recurse into itself and refused to run — see Use it as git. | | 127 | The real git isn't on PATH (set FNGIT_GIT to point at it directly). | | other | A passed-through git invocation's own exit status. |

Everything that isn't a decorated clone is git, verbatim — same flags, same stdout/stderr, same exit code, same behavior for a signal that kills it.

fngit install

fngit install — in every shape, including an unrecognized flag — is fngit's own command, never passed through to git (a plain fngit install with no git equivalent would otherwise just error out of git itself).

fngit install                    # interactive wizard
fngit install -y                 # never prompts — recommended values, or
                                  # whatever's already configured, everywhere
fngit install -y --clone-template '~/src/{repo}@{owner}' \
  --worktree-template '~/src/{repo}@{owner}+{input}' \
  --additional-src-dirs '~/code,~/dev' \
  --host-alias git.example.com=ex \
  --no-plugin --shadow-git
fngit install --dry-run          # print the plan, write nothing
fngit install --remove-shadow    # remove the git shim and its PATH blocks

| Flag | Meaning | | -------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | | --clone-template <t> / --worktree-template <t> | Override those two templates. | | --additional-src-dirs <a,b> | Comma-separated; repeatable, appending each time. | | --host-alias <host>=<alias> | Repeatable. | | --plugin / --no-plugin | Install the worktree-paths Claude Code plugin. | | --shadow-git / --no-shadow-git | Put a git shim first on PATH (see below). | | -y, --yes | Never prompts — every unanswered question takes its recommended value, or whatever's already configured. | | --dry-run | Print the plan (describePlan) instead of writing anything. | | --remove-shadow | Remove the shim and every PATH block it added, then exit. | | --help | Print usage. |

With no flags and an interactive terminal, it prompts for whatever isn't already configured, showing each recommended value as the default. Non-interactive without -y and with an unanswered question is an error (exit 2).

Shadowing git via a PATH shim (--shadow-git): a git shim script (exec fngit "$@" on POSIX, git.cmd → @fngit %* on Windows) is written to $XDG_DATA_HOME/rhombus.rocks/fngit/shims (default ~/.local/share/rhombus.rocks/fngit/shims), and an idempotent marked block prepending that directory to PATH is written to ~/.bashrc, ~/.zshrc, the fish conf.d, and the PowerShell profile — whichever exist. resolveRealGit skips that directory when searching PATH, so the shim never resolves to itself. --remove-shadow undoes both.

The plugin (--plugin, on by default when claude is on PATH): installs worktree-paths from the rhombus-rocks/claude-plugins marketplace, detecting and swapping an old claude-code-worktree-paths@fnrhombus-plugins install first. Re-running fngit install -y is a no-op wherever nothing changed — settings, the shim, and the plugin state are each written only when they'd actually change.

Driving it programmatically: fngit install is a thin CLI shell over a pure plan builder, exported from the package so fnc install (or any other caller) can drive the same logic without spawning a subprocess or prompting anyone itself:

import { buildInstallPlan, type InstallAnswers,
  type InstallEnv } from '@rhombus.rocks/fngit';

const plan = buildInstallPlan(answers, /* InstallAnswers */
  env /* InstallEnv */);
// plan: readonly InstallAction[] — { kind, description, ...effect-specific fields }[]

resolveInstallAnswers(options, env, prompter) turns InstallOptions (parsed CLI flags) plus an IPrompter into InstallAnswers; buildInstallPlan turns InstallAnswers plus InstallEnv (current settings, resolved config path, shim dir, shadow targets, plugin state) into the ordered InstallAction[] plan — write-settings, write-shim-script, write-shadow-block, sync-plugin — each carrying enough to execute or describe it. Also exported: parseInstallArgs, needsPrompting, currentHostAliasOverrides, describePlan, buildRemoveShadowPlan, the RECOMMENDED_* defaults, and INSTALL_HELP.

Settings

fngit reads a shared config file at $XDG_CONFIG_HOME/rhombus.rocks/config.{json,jsonc,toml,yaml} (default ~/.config/rhombus.rocks/config.json) — the first of those four extensions that exists, in that order, parsed with confbox. FNGIT_CONFIG=<path> overrides the location outright, in whichever of those formats that path names.

{
  "$schema": "https://json.schemastore.org/rhombus-rocks-config.json",
  "repos": {
    "cloneTemplate": "~/src/{repo}@{owner}",
    "worktreeTemplate": "~/src/{repo}@{owner}+{input}",
    "additionalSrcDirs": ["~/.local/src", "~/code"],
    "hostAliases": { "git.example.com": "ex" }
  }
}

repos.cloneTemplate, repos.worktreeTemplate and repos.additionalSrcDirs are read exactly as before (the last accepting a single path or a list). repos.branchTemplate is read only by the worktree-paths Claude Code plugin — fngit doesn't read it, but preserves it whenever it writes this file. Every failure degrades silently: an unreadable, malformed, or wrong-shaped file (or field) contributes nothing rather than failing the lookup.

Host aliases for the {host-short} placeholder ship with built-in defaults — github.com=gh, gitlab.com=gl, bitbucket.org=bb, codeberg.org=cb — overridden per-host by repos.hostAliases. A host with neither a built-in default nor an override fails with an error naming repos.hostAliases.

Migration: ~/.fngitrc (the older, flatter JSON shape — cloneTemplate, worktreeTemplate, additionalSrcDirs, hostAliases at the top level, no repos nesting) is read only when the new file is absent, and only by locate()/fngit clone; fngit install moves its contents into the new file. Once the new file exists, ~/.fngitrc is never consulted again.

Platforms

Linux, macOS, and Windows are supported. CI tests all three on every PR.

Templates are written with forward slashes (e.g. ~/src/{repo}@{owner}); expansion produces native paths via path.join/path.normalize. The config file's location is computed with path.join, so it lands at the same .config/rhombus.rocks/config.json-shaped path (native separators) on every platform.

Development

Toolchain is pinned via mise — running any command inside the repo picks up the pinned Node and bun versions automatically.

bun install
bun run lint          # typecheck + eslint
bun run test          # bun test
bun run build         # tsc emit to dist/
bun run format        # dprint fmt
bun run format:check  # dprint check

A pre-commit hook (.githooks/pre-commit, wired up automatically by mise on directory entry) runs dprint check and bun run lint before every commit.

Release

Every PR merge to main runs the Release workflow's publish job: semantic-release reads the conventional-commit history, determines the next version, publishes it to npm under the @latest dist-tag with provenance, and creates the GitHub release.

See CLAUDE.md for the full branch policy, TDD requirement, and commit conventions this repo follows.