Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Concepts

Grimoire borrows its mental model from package managers you already use, then swaps the transport for an OCI registry. This page covers the handful of ideas that make the commands feel obvious.

Skills, rules, and agents

Grimoire distributes three kinds of installable artifact. A skill is a directory — a SKILL.md plus any supporting scripts or references — that teaches an agent a capability. A rule is a Markdown file that states a standard or constraint the agent should always follow. An agent is a Markdown file that defines a delegatable assistant — a system prompt with a name, description, and tool access; see Agent Artifacts.

All three are declared the same way and travel through the same pipeline; the differences are shape on disk (a folder versus a file) and the kind argument (skill, rule, or agent) you pass to commands like grim add.

Rules with a support directory

A rule is often an index that points at extra context — a worked example, a JSON schema, a script — that would clutter the rule itself. The convention across AI-config tooling is to put that next to the rule in a sibling folder with the same name (rules/my-rule.md referencing ./my-rule/examples.md).

A bare index whose links resolved to nothing would be useless, so a rule may carry an optional support directory: a folder beside the index sharing its stem (my-rule.md + my-rule/). Grimoire packs the index and every file under that folder into the one artifact, and installs them beside each other so the index’s relative links resolve:

.claude/rules/my-rule.md
.claude/rules/my-rule/examples.md
.claude/rules/my-rule/schema.json

A rule with no support directory is unchanged — it remains the single my-rule.md file. See Publishing for how the directory is packed.

Artifacts as OCI content

Under the hood every skill, rule, or agent is packed into an OCI artifact and addressed by content digest, exactly like a container image layer. Identical content is stored once and is immutable: a sha256:… digest always names the same bytes.

Each artifact declares its kind through a com.grimoire.kind manifest annotation — skill, rule, agent, or bundle — so grim (or any OCI-aware tool that reads manifest annotations) can tell a Grimoire artifact apart without unpacking it. The manifest’s config descriptor is the OCI empty config, which keeps the wire format acceptable to every registry, including GitLab (see Registry compatibility).

This is why Grimoire needs no server of its own. Any registry that speaks the distribution specGHCR, Docker Hub, a private Distribution — is a complete backend.

References, tags, and digests

You name an artifact with a reference: registry/repository:tag, or registry/repository@sha256:… for an exact digest. A floating tag such as :1 points at the newest 1.x release and moves over time; a digest never moves.

You declare floating tags for convenience and let Grimoire pin them to digests for reproducibility — which is the job of the lock.

The lock

grimoire.lock records the exact digest each declared tag resolved to, so an install is byte-for-byte reproducible until you deliberately upgrade. grim lock resolves the floating tags in grimoire.toml; grim update re-resolves them and rolls the pins forward when a newer version appears behind the same tag.

The lock also stores a hash of the declaration it was generated from, so Grimoire can tell when grimoire.toml has drifted ahead of the lock.

Bundles

Declaring the same dozen skills and rules in every repository does not scale. Teams end up copying a block of grimoire.toml between projects, and when the approved set changes someone has to chase down every copy.

A bundle is a curated set of members — skills, rules, and agents — published as its own OCI artifact. You declare the bundle once in [bundles], and on grim lock it expands into its members, which are pinned into the lock exactly like a direct declaration. Update the published bundle, re-lock, and every project that declares it moves together.

Each locked member records its provenancedirect for something you declared yourself, or the bundle it came from — which grim status surfaces so you always know why an artifact is installed.

Adding and dropping members

The bundle’s member list is authoritative on every resolve, so membership tracks the published bundle. When a new bundle version adds a member, the next grim lock expands it into the lock and the next install materializes it. When a version drops a member, that member leaves the lock — and grim update prunes its materialized files, unless you have edited them locally, in which case it is kept until you re-run with --force. This is the same reconciliation any artifact leaving the lock receives; bundles just make it routine.

Conflict policy

Because a member is keyed by (kind, name), two sources can name the same slot. Grimoire resolves that deterministically:

  • a direct [skills]/[rules]/[agents] declaration always wins over any bundle — this is how you override a single member without forking the bundle;
  • two bundles that name a member at the same identifier coalesce to one entry;
  • two bundles that disagree fail closed: grim lock stops with a conflict error and asks you to declare the member directly to choose one.

Failing closed is deliberate. Silently picking a winner would let an unrelated bundle bump change what a project installs without anyone noticing.

Floating versus pinned members

A bundle’s members can themselves be floating tags or exact digests. A floating member is re-resolved fresh on every consumer grim lock, so reproducibility comes from the consumer’s own lock. Publishing with grim release --pin instead freezes every floating member to a digest at publish time, so the bundle is reproducible on its own — the stronger guarantee for air-gapped or tunneled networks that cannot re-resolve a tag. See Publishing.

Scopes

Grimoire works in two scopes. The project scope is the grimoire.toml discovered from the current directory — per-repository configuration that lives beside your code. The global scope is a single config under $GRIM_HOME for artifacts you want everywhere.

Most commands operate on the discovered project by default and switch to the global scope with --global. The TUI can flip between the two at runtime.

Clients

An installed artifact has to land somewhere the agent reads. Grimoire calls that destination a client target and ships three: Claude Code, opencode, and GitHub Copilot. The same skill is transformed into each client’s native layout on install.

grim install writes to the targets listed in the clients option in your config, defaulting to ["claude"]; --client overrides it and accepts a comma-separated list to install into several AI clients at once.

The catalog

grim search and the TUI read a catalog — an index of the artifacts a registry offers, cached locally under $GRIM_HOME so repeat browsing is fast and works offline. Pass --refresh to rebuild it from the registry.

Online by default, offline on demand

By default Grimoire is online: every floating-tag lookup resolves fresh against the registry, and the resolved digest is cached as a write-through so later offline runs still work. A floating tag therefore never serves a stale pin — there is no “use the cache first” mode to surprise you, and no --remote flag to remember.

Pass --offline to flip to cache-only: Grimoire forbids all network access and fails rather than touch a registry — useful in sealed CI or an air-gapped network. Warm the cache with a normal online grim lock (or grim update) before going offline. --offline has an environment-variable equivalent described in Configuration.