okf-gem · docs
CLI · serve & setup

okf registry

Name a bundle once, use it from anywhere.

When to use it

  • You work across more than one bundle. Register each once and every verb reaches it by name: okf lint @handbook works from any directory, no path required. Many bundles, one registry walks the setup end to end.
  • You want the names to travel with the repo. okf registry init drops a project-local .okf-registry.json you commit, so a teammate who clones gets the same @slugs and a bare okf server works with no global setup on the machine.
  • You want one name for a set of bundles. okf registry group backend @orders @billing @shared gives the set a single slug you search and serve as a unit.
  • You want one graph page over all of them. A bare okf server hosts every registered bundle behind one hub, and Cmd/Ctrl-K switches between them; okf server has the details.
  • You ask cross-bundle questions. okf search @all <term> ranks matches across every registered bundle in one query, each row labeled with the bundle that answered; okf search covers it.

How it works

okf registry manages a plain JSON file at $OKF_HOME/registry.json, with $OKF_HOME defaulting to ~/.okf. It stores references, never content: a path, a slug, and a title per bundle. The bundles stay where they are on disk, owned by the repos they document, so nothing is copied and nothing can go stale except the path itself. The file is meant to be read, grepped, and hand-edited.

okf registry init                    # create a project-local .okf-registry.json here
okf registry list                    # every entry; * marks the default
okf registry set <dir> [--as SLUG]   # add or update (identity is the path)
okf registry del <dir|@slug>         # remove the entry (or a group); the bundle stays on disk
okf registry default @slug           # move an entry to the front
okf registry rename <slug> <new>     # change the name, keep the position
okf registry group <slug> <@member> # name a set of bundles or groups; nests
okf registry ungroup <slug> <@member> # remove members; emptying deletes the group

The slug is the bundle's name everywhere. It is minted from the directory basename unless you choose one with --as. The two paths differ on collision, deliberately: a minted name that is taken gets a suffix (docs becomes docs-2), because you never asked for it, while a name you chose with --as or rename is refused instead, because silently serving a different one would be a lie. all is reserved on every path in, since @all already means every registered bundle.

The list is ordered, and the first entry still on disk is the default: the bundle a bare okf server opens at /, and the one a bare @ names. registry default @slug moves an entry to the front, and registry set --default registers straight to it. Nothing else is stored, so nothing else can drift: a rename keeps its position, a del promotes whatever is next, and the file cannot name a default that is not in it.

A registry can live in the repo

okf registry init creates a project-local .okf-registry.json in the current directory. Once it exists, okf discovers it by walking up from wherever you are, and every registry operation and every @slug resolves through it instead of the global $OKF_HOME one, so a bare okf server inside a repo serves that repo's bundles with no global setup on the machine. The nearest registry on the path wins, since nested ones resolve nearest-first; okf registry list names the local file it found, and OKF_NO_DISCOVERY=1 forces the global one, the escape hatch for a fixed-directory caller such as CI.

A project-local registry stores portable paths. A bundle inside the registry's own tree is written relative to the .okf-registry.json, so the file can be committed and a checkout on another machine, or a container that mounts it, resolves the same bundles unchanged. A bundle outside the tree keeps an absolute path, because it cannot travel. Paths still read back absolute everywhere the CLI reports them, the relative form lives only on disk, and an existing absolute local entry migrates to relative on its next write. The global $OKF_HOME registry is unchanged and stores absolute paths as before.

Groups: one slug for a set of bundles

A group is a registry slug that names a list of members, and a member can be a bundle or another group, so groups nest; it resolves recursively and path-deduped down to its bundle leaves. okf registry group <slug> <@member…> creates one or adds to it, ungroup removes members (and emptying a group deletes it), and del and rename reach a group slug too, so one rename cascades across every member list and one del drops the slug and deletes any group it empties. okf search @backend merges the members into one ranking and okf server @backend mounts each of them, the first at /, both skipping a vanished member with a note exactly as @all does. Every single-bundle verb refuses a group and exits 2, the same rule that refuses a second bundle.

@slug, wherever a directory goes

Registering gives a bundle a name the whole CLI understands. Wherever a verb takes a <dir>, @slug names a registered bundle and a bare @ names the default:

okf lint @handbook                 # from anywhere, no path
okf render @ -o graph.html         # the default bundle, exported
okf search @all rate limit         # every registered bundle, ranked together
okf server @handbook @wiki         # a hub of exactly these two

The registry-editing verbs (del, default, rename) take the slug bare or with the @; the reading verbs need the @, since a bare word there is a path.

Try it

okf registry set ./docs --as handbook
registered handbook → /Users/you/work/billing/docs (37 concepts)
okf registry list
* handbook  billing/docs   (/Users/you/work/billing/docs)
  wiki      team/wiki      (/Users/you/work/team/wiki)

From here okf server with no arguments serves both behind one hub, and any verb takes @handbook in place of the path.

Pitfalls

  • A running server does not follow the file. The hub reads its bundles at boot, so registering, renaming, or deleting one needs a server restart to show.
  • A discovered registry outranks $OKF_HOME. When a .okf-registry.json sits at or above your working directory, it wins and every verb resolves through it; $OKF_HOME names the global registry, used only when no local one is found or when OKF_NO_DISCOVERY=1 forces it. $OKF_HOME still names exactly one registry, with no fallback to ~/.okf behind it, and an empty value counts as unset.
  • A single-bundle verb refuses a group. okf lint @backend exits 2 when @backend is a group, the same as pointing it at two bundles; search and server are the verbs that take a set.
  • Committing a local registry rewrites its own paths once. An existing absolute local entry migrates to relative on its next write, so a .okf-registry.json you already had can rewrite its paths the first time you touch it. In-tree bundles go relative so they travel; out-of-tree bundles stay absolute.
  • Deleting from the registry deletes nothing on disk. del removes the reference; the bundle stays where it always was. The reverse also holds: deleting a directory does not prune its entry. registry list marks it missing and leaves the decision to you, and the default quietly skips it, since the star must name a bundle / can actually open.
  • A path argument names a location, only a location. registry del ./notes matches an entry by that path and never falls through to the slug notes, so you cannot remove a bundle that merely shares a name with a local directory.
esc
navigate open