GhostPaw

@ghostpaw/grimoire 0.3.0

Your agent edits its SKILL.md. Who keeps the history?

grimoire keeps skills as plain SKILL.md folders, seals each proven improvement as a git commit and keeps the evidence notes in SQLite. A skill's rank is the number of sealed commits, so an edit counts only once it is sealed, and any earlier version can be restored.

As a library

As a local MCP server

Replay the run Read the source on GitHub

Free and MIT licensed for Node.js 24 or newer, with zero runtime dependencies. Spells are files on your disk, history is a git repository next to them, and metadata is SQLite.

An open book drawn in cyan light, a ghost paw resting on the right-hand page among lines of glyphs, a violet hexagonal rune floating above.

0runtime dependencies

One spell, seven calls

The agent's edit waits, uncommitted, until somebody seals it.

This is the deploy-service spell from the README quick start, replayed from a real run: we made these calls against grimoire 0.3.0 on 2026-10-24 with the git date fixed to 10:00 UTC, so the commit hashes are the real ones. Run the steps in order.

Spell

  • Spellnot inscribed
  • Rank—
  • Commits0
  • Notes0
  • Uncommitted0
  • Step 1—

An empty grimoire after init(root, db).

  1. 01 Inscribe a file without frontmatter

    A spell has to be a valid SKILL.md.

    write.inscribe(root, db, {
      name: 'deploy-service',
      chapter: 'engineering',
      content: '# deploy-service\n\nRun the build, push, apply.\n',
    });
    // GrimoireValidationError
    // Invalid SKILL.md content: Missing opening frontmatter delimiter (---)

    Refused, and no folder was created. This is a format check, not a security scan.

  2. 02 Inscribe the README spell

    Same call with frontmatter, as in the quick start.

    write.inscribe(root, db, {
      name: 'deploy-service',
      chapter: 'engineering',
      content: '---\nname: deploy-service\ndescription: Deploy a service to production\n---\n\n'
        + '# deploy-service\n\n## Steps\n\n1. Run `npm run build`\n2. Push to registry\n3. Apply manifests\n',
    });
    // { spell: { path: 'engineering/deploy-service', rank: 1, tier: 'Apprentice',
    //   description: 'Deploy a service to production', bodyLines: 9, ... }, warnings: [] }

    The folder engineering/deploy-service/ now holds a SKILL.md, and the first commit gives it rank 1.

  3. 03 Drop a note from practice

    Evidence comes first and waits.

    write.dropNote(db, {
      source: 'practice',
      content: 'Build step fails if DOCKER_REGISTRY is not exported first.',
    });
    // { id: 1 }

    One note pending. The spell has not changed.

  4. 04 The agent edits the spell

    It adds the missing step at the top.

    write.updateSpell(root, 'engineering/deploy-service', newContent);
    read.pendingChanges({ root }, 'engineering/deploy-service');
    // [{ spellPath: 'engineering/deploy-service',
    //    changes: [{ status: 'modified', filePath: 'engineering/deploy-service/SKILL.md' }] }]

    The edit is on disk and still uncommitted. Rank stays at 1.

  5. 05 Seal the improvement

    Sealing is the moment someone says the edit is proven.

    write.seal({ root }, db, ['engineering/deploy-service'],
      'Fix: document required env var for build step');
    // { commitHash: '26141d857cead85fd67855a7f7574ab7ed454764',
    //   sealedPaths: ['engineering/deploy-service'],
    //   ranks: { 'engineering/deploy-service': 2 } }

    A real git commit, and rank 2.

  6. 06 Distill the note

    The note is now part of the spell.

    write.distill(db, 1, 'engineering/deploy-service');
    read.pendingNotes(db).length
    // 0

    No notes pending.

  7. 07 Read the history and roll back

    Say the new first step turns out to be wrong.

    const log = read.history({ root }, 'engineering/deploy-service');
    write.rollback({ root }, 'engineering/deploy-service', log.at(-1).hash);
    // history
    // 26141d8  Fix: document required env var for build step
    // c40ce35  inscribe engineering/deploy-service
    
    // { success: true, restoredRef: 'c40ce353576648772363ce66a2568bcf72d67d7c' }

    The first version is back on disk as an uncommitted change, and rank stays at 2 until that change is sealed.

After the run, the inspect_grimoire_item tool reported “Inspected spell engineering/deploy-service (Apprentice, rank 2).” with one seal to the next tier.

Tier names come from the library at read time. The tier table in the README gives different rank boundaries from the code, so this page only shows what the run returned.

Eight words for one skills folder

A branching tree of glowing nodes in dashed tiers, cyan lines climbing upward and one violet node at the top right.

Files for content, git for history and rank, SQLite for the rest.

Spell
A learned procedure stored as SKILL.md, YAML frontmatter plus a markdown body.
Chapter
A folder of related spells, such as engineering/.
Seal
A git commit that marks a proven improvement. Rank is the number of seals.
Tier
A name computed from rank on every read.
Note
Raw evidence, such as an observation, a failure path or a correction, waiting to be distilled.
Resonance
A read-time signal for which spell is most worth improving next.
Draft
A proposed new spell held for review before it is inscribed.
Catalogue
A health pass over the grimoire covering staleness, dormancy, oversized spells and note routing.

Each of the three stores is optional, and the package degrades without any of them, as the README says. Without git there are no seals, so rank and rollback go too.

If skills already feel like a supply-chain risk

Marketplace skills have shipped with vulnerabilities, and a SKILL.md can tell an agent to do things you never asked for. grimoire does not fix that, and does not claim to.

  • It validates the format of a SKILL.md. It does not judge what the instructions say.
  • Every change to a spell is either uncommitted or a commit you can read, diff and roll back.
  • scout_skills can adopt spells from outside sources. Treat those like any other code you pull in.
  • Eleven tools with JSON Schemas, among them update_spell, hone_spell, manage_spell and run_catalogue.
  • Node.js 24 or newer, because it uses the built-in node:sqlite, and git on the machine for seals.

What it costs to add

Runtime dependencies
0
Node.js
24 or newer (node:sqlite)
Size of dist/index.js, gzip
46.4 KB
Import time
about 9.4 ms
Tests
872 passing
Line coverage
95.6%
Agent tools
11

Measured 2026-10-24 on the maintainer's machine with Node.js 24.14.0 against grimoire 0.3.0: size of the published dist/index.js at gzip -9, median of 11 cold imports, tests and coverage from the v0.3.0 tag.

Start from the README

import { DatabaseSync } from 'node:sqlite';
import { init, read, write } from '@ghostpaw/grimoire';

const db = new DatabaseSync(':memory:');
const root = '/path/to/my-grimoire';

// filesystem + SQLite tables + bare git repo with initial commit
init(root, db);

const { spell } = write.inscribe(root, db, {
  name: 'deploy-service',
  chapter: 'engineering',
  content: '---\nname: deploy-service\ndescription: Deploy a service to production\n---\n\n# deploy-service\n\n## Steps\n\n1. Run `npm run build`\n2. Push to registry\n3. Apply manifests\n',
});

write.dropNote(db, {
  source: 'practice',
  content: 'Build step fails if DOCKER_REGISTRY is not exported first.',
});

write.seal({ root }, db, [`engineering/${spell.name}`], 'Fix: document required env var for build step');

const detail = read.getSpell(root, 'engineering/deploy-service', db);

To run it as an MCP server, add npx -y @ghostpaw/grimoire mcp to your client.