Your agent keeps its state in markdown files.
MEMORY.md, TODO.md, USER.md, SOUL.md. They grow every session, nobody prunes them, and one wrong line steers every run after it.
GhostPaw is seven small Node.js libraries, one for each of those files and for the skills, inbox and chats around them. Each keeps its part of the state as SQLite records inside your agent's own process, so you can read, correct and delete what the agent knows while it runs without you.
See all seven How they fit together
Free, MIT licence, Node.js 24 or newer. All seven have zero runtime dependencies. No account and no service to run.

0runtime dependenciesin all seven packages
One library for each file your agent keeps
Each row names the file it takes over, what it gives an agent that runs with nobody watching, and the one command that installs it.
-
Replaces
MEMORY.mdcodex 0.3.2
Beliefs instead of a memory file
MEMORY.md stops being a junk drawer: every belief carries its source and certainty, stale ones are flagged when read, and a wrong fact gets corrected with its lineage kept.
One record holds:
- claim
- source: explicit, observed or inferred
- certainty and evidence count
- freshness
- lineage of corrections
-
Replaces
TODO.mdquestlog 0.6.2
Quests instead of a to-do list
An agent that said it was done has to write down what came of it, and a blocked quest stays blocked until the one before it succeeds.
One record holds:
- title and objective
- dueAt or a scheduled window
- unlocks that block other quests
- outcome text on finish
- repeat rule (RRULE)
-
Replaces
USER.mdaffinity 0.3.0
Contacts instead of a user note
The people your agent deals with, with their accounts, a journal of what happened, the promises made and the dates that matter, plus a radar of who is drifting out of touch.
One record holds:
- contact and identities (email, phone, account)
- social link: rank, trust, cadence
- ties such as works_at
- journal entries as evidence
- date anchors such as birthdays
-
Replaces
SOUL.mdsouls 0.3.0
An identity that changes with evidence
Character that moves only on evidence: refinements wait for level-up, and any of them can be rolled back.
One record holds:
- essence
- traits with evidence
- level-up consolidation
- full rollback
-
Replaces
skills/grimoire 0.3.0
Procedures that get better and keep their history
Procedures stay SKILL.md files, each verified improvement is a git commit, and a health check names stale, dormant and oversized skills.
One record holds:
- spell: a SKILL.md file
- seal: a git commit per verified improvement
- tier from seal count
- notes awaiting distillation
- catalogue health snapshot
-
Replaces
inbox/email 0.4.1
A mailbox the agent can search offline
Your agent can read and search the mailbox. Whether it can also send is a separate tool, and you decide.
One record holds:
- account with IMAP and SMTP config
- folder with sync state
- message headers, flags, labels
- body and attachments
- thread tree
-
Replaces
chat logtelegram 0.6.2
Every Telegram update kept before your handler runs
Every update lands in SQLite before your handler runs, edits keep the old text, and the agent's tools only plan until you switch execution on.
One record holds:
- message mirror with edit history
- reply chains
- FTS5 search with diacritic folding
- file bytes as BLOBs, stored once per SHA-256 hash
- plan-first agent tools
How they fit together
The packages never import each other. Your agent's code carries data from one to the next, and each keeps its own records, in its own tables.
Here is one afternoon of an agent that runs a small renewal desk on its own.
telegramLena posts “Invoice 4471 is paid” in the Renewal desk group, and telegram has the row before your handler sees it.
affinityYour handler looks her up by Telegram account and adds the exchange to her journal.
codexThe agent writes “invoice 4471 is paid” as an explicit belief. Yesterday's belief said it was overdue. That one is superseded, not deleted, so you can still see what the agent thought before and why it changed.
questlogThe quest “Chase invoice 4471” closes only with a written outcome: paid, confirmed by Lena in the group.
emailIt finds the invoice thread in the cached inbox with mail_search and labels it paid with mail_organize. A reply to the vendor would need mail_compose, which this agent was never given.
grimoireIt followed the steps in a SKILL.md for closing a paid invoice. Last time the receipt went to the wrong folder, and that note is still attached. When a better version proves itself, it is sealed as a git commit you can diff and roll back.
soulsWeeks of runs like this one pile up evidence for a trait: confirm payment with a person before closing a quest. The essence only changes at level-up, and you can roll the change back.
~/.ghostpaw/
telegram.dbLena's message, edit keptaffinity.dbjournal entry for Lenacodex.dbbelief superseded, lineage keptquestlog.dbquest done, outcome writtenemail.dbthread labelled paidgrimoire/note on a SKILL.md, then a sealsouls.dbevidence for one trait
An illustration of one way to wire them, not a recorded run. Each library's own page replays a real run against its current release.
What all seven share
- Runtime
- Node.js 24 or newer and its built-in
node:sqlite. Zero runtime dependencies, MIT licence. - Ways in
- A library import, an MCP server started with
npx -y @ghostpaw/<name> mcp, aghostpaw-<name>command line tool and an Agent Skill. To connect these tools to a model in your own code, chatoyant is a zero-dependency OCaml/TypeScript LLM SDK by the same author, with guides on structured outputs, JS LLM SDK bundle size and calling LLMs from OCaml. - Where state lives
- A SQLite file you can open. The MCP servers default to
~/.ghostpaw/<name>.db; grimoire also keeps its SKILL.md files in a git folder. - Taking one
- Install one package and ignore the rest. None of them needs another to work.
If an agent working alone worries you
Plenty of developers switch agent memory off, because unread memories pile up and old lines win arguments, and an agent that also sends mail, posts in groups and rewrites its own instructions while you sleep is a bigger worry than one that only remembers.
Here is what each package does about it.
- Every record is a row in a SQLite file your own process opens. You can read and delete it with
sqlite3. - codex flags a belief nobody confirms for 90 days as stale, and recall calls no model.
- questlog's
run_questtool refuses to start a quest whose prerequisites are unmet, unless the call passesforce. - email keeps reading and sending in separate tools. Leave
mail_composeout and the agent cannot send. - telegram's send and moderation tools return plans until you switch execution on.
- souls and grimoire keep every change to identity and skills as history you can roll back.
All seven, side by side
| Package | Takes over | Version | Runtime deps | Tests | gzip | Import |
|---|---|---|---|---|---|---|
@ghostpaw/codex | MEMORY.md | 0.3.2 | 0 | 380 | 31.5 KB | 2.5 ms |
@ghostpaw/questlog | TODO.md | 0.6.2 | 0 | 352 | 42.1 KB | 3.7 ms |
@ghostpaw/affinity | USER.md | 0.3.0 | 0 | 459 | 56.1 KB | 4.8 ms |
@ghostpaw/souls | SOUL.md | 0.3.0 | 0 | 532 | 31.4 KB | 2.8 ms |
@ghostpaw/grimoire | skills/ | 0.3.0 | 0 | 872 | 46.4 KB | 7.6 ms |
@ghostpaw/email | inbox/ | 0.4.1 | 0 | 563 | 47.8 KB | 10.4 ms |
@ghostpaw/telegram | chat log | 0.6.2 | 0 | 887 | 34.2 KB | 8.9 ms |
Measured 2026-10-24 on the maintainer's machine with Node.js 24.14.0 against the version shown: tests from node --test at the release tag, size as gzip -9 of the published dist/index.js, import time as the median of 11 cold imports.