jotbook
v0.0.1
Published
Markdown-based project memory for Claude Code, viewable in your browser.
Maintainers
Readme
Jotbook
Plain-Markdown project memory for Claude Code, viewable in your browser.
jotbook is a tiny Claude Code extension that turns project memory into a folder of plain Markdown files (one per entry) and ships a static HTML viewer so you can read and filter your memory in the browser while you work.
- Plain Markdown all the way down. Entries are standard
.mdfiles with YAML frontmatter — the same shape Claude Code's auto-memory and OpenAI CodexAGENTS.mduse. They render correctly on GitHub, in any editor, in any Markdown reader. Drop your own.mdfiles intoentries/and they just work. - No bespoke syntax. No custom fence types, no raw-HTML escape hatches, no widget DSL. If a Markdown reader can render it, jotbook can render it.
- O(1) writes. Each new memory entry is one new file. Claude never has to read the whole memory or re-emit a JSON blob to add an entry.
- Selective reading. A small generated
memory.jsonindex lets Claude filter bytypeandtagsand load only the relevant entry bodies on demand. - No build step for users. The viewer ships pre-built —
npx jotbookis everything end-users run. (Maintainers runnpm run buildbefore publishing to regenerate the React bundle; seeviewer-src/and ADR 0018.)
Quick start
cd your-project
npx jotbook init # scaffold .claude-memory/, skill, /remember command, hook
npx jotbook serve # open the viewer at http://localhost:4123Then use Claude Code in any of these ways:
/remember we use JWT v2 for auth — see src/auth.ts:10-40or just say it in natural language:
"Remember that we use JWT v2 for auth — see src/auth.ts:10-40."
Either path writes a new file at .claude-memory/entries/<id>.md. Refresh the viewer to see it.
Commands
| Command | What it does |
| --- | --- |
| npx jotbook init | Scaffold .claude-memory/, install the skill, install the /remember command, register the SessionStart hook. |
| npx jotbook build | Rebuild .claude-memory/memory.json from entries/*.md. |
| npx jotbook update | Refresh the viewer bundle, schema, skill, and slash command. Your entries and viewer-config.json are preserved (v3 is a clean break — see ADR 0016 — so there's no migration pass; legacy v1 fields on disk surface as _warnings on the Index stub and need a manual cleanup). |
| npx jotbook serve | Serve the viewer at http://localhost:4123. Regenerates memory.json on every fetch, so edits to entries show up on refresh. |
| npx jotbook record --title "..." [--type ...] [--body ...] | Create a new Entry through the audited write path (id is invented; v2 schema enforced). |
| npx jotbook validate | Walk entries/*.md and exit non-zero on any schema error. Designed for CI. |
| npx jotbook codex-notify | Codex notify hook surface — validates agent-turn-complete events and exits cleanly. |
| npx jotbook --version | Print the installed version. |
What init creates
your-project/
├── .claude-memory/
│ ├── entries/ ← one .md file per entry — your memory lives here
│ ├── assets/ ← images referenced by entries
│ ├── memory.json ← generated index. DO NOT hand-edit.
│ ├── schema.json ← JSON Schema for the index (the contract)
│ ├── viewer.html ← static viewer
│ ├── viewer.js
│ ├── viewer.css
│ ├── entry-format.js ← shared MD-entry parser (used by CLI + viewer)
│ └── viewer-config.json ← customize colors, labels, types
└── .claude/
├── settings.json ← SessionStart hook entry
├── commands/
│ └── remember.md ← /remember slash command
└── skills/
└── jotbook-memory/
└── SKILL.md ← teaches Claude the MD entry formatThe entry format
Every memory entry is a plain Markdown file with YAML frontmatter:
---
id: 2026-05-14-jwt-v2
type: decision
title: Use JWT v2 for auth
tags: [auth, architecture]
created: 2026-05-14T10:00:00Z
updated: 2026-05-14T10:00:00Z
---
We switched to JWT v2 because the v1 library is unmaintained. Tokens are signed
with RS256; rotation is monthly. See `src/auth.ts:10-40`.That's it. Frontmatter + plain Markdown. Nothing else.
Required frontmatter fields: id (must match the filename), type, title. Everything else is optional.
Common type values: note, decision, todo, convention, code-area, correction. Free-form is fine; the viewer falls back to default styling.
v2 vocabulary (the only allowed frontmatter keys): id, type, title, author, tags, links, evidence, supersedes, resolves, created, updated. The schema validator (bin/schema-validator.js) rejects writes containing anything else — including the v1 fields status, assigned_to, blocks, manual, verified, which were removed in 2.0.
What the Markdown body can contain
Standard Markdown — the same subset that renders correctly on GitHub and in most editors. No special syntax:
- Headings (
#–######), paragraphs, bullet lists, bold, italic, inline code, links - Blockquotes (
> …) for asides, callouts, and warnings - Images:
— save the file under.claude-memory/assets/ - Fenced code blocks with language tags
- Mermaid diagrams via a standard
```mermaidfence (lazy-loaded) - Pipe tables:
|col|col|with a|---|---|separator row - Horizontal rule:
---
Any other tool that reads Markdown will render the same file the same way. That's the point.
The generated index (memory.json)
memory.json is a build artifact. The CLI regenerates it from entries/*.md on every serve and build. Do not hand-edit it.
{
"version": "3.0",
"project": "your-project",
"entries": [
{
"id": "2026-05-14-jwt-v2",
"type": "decision",
"title": "Use JWT v2 for auth",
"tags": ["auth", "architecture"],
"created": "2026-05-14T10:00:00Z",
"updated": "2026-05-14T10:00:00Z",
"file": "entries/2026-05-14-jwt-v2.md"
}
]
}The index stays small — title and frontmatter only. The full entry body is in the .md file and only loads when someone opens that specific entry in the viewer or when Claude reads it for selective recall.
Customizing the viewer (no forking required)
Edit .claude-memory/viewer-config.json:
{
"title": "Acme Memory",
"theme": {
"primary": "#ff5a5f",
"background": "#fff",
"surface": "#f4f4f4",
"text": "#111",
"muted": "#666",
"border": "#e0e0e0"
},
"types": {
"note": { "label": "Note", "color": "#5b8def" },
"decision": { "label": "Decision", "color": "#a371f7" },
"spike": { "label": "Spike", "color": "#f0883e" }
},
"defaultSort": "updated-desc"
}Any new type value you start using just needs an entry here to get a custom label and color. Unknown types still render fine.
Environment
JOTBOOK_PORT— port forserve(default4123).
Why plain Markdown?
- Portable. Any
.mdfile someone wrote in another tool drops intoentries/and renders. Any jotbook entry opens in GitHub, VS Code, Obsidian, orcatand looks the same as in the viewer. - O(1) writes. Adding a memory entry is creating one new file — no read-parse-mutate-write cycle on a growing JSON file.
- Sticky for the agent. Markdown with YAML frontmatter is the format every major coding agent (Claude Code's auto-memory, Codex
AGENTS.md, Cursor) chose for the same reason: agents write it more reliably than JSON. - Selective reading still works. The generated
memory.jsonindex lets Claude filter bytypeandtagsand load only the bodies it actually needs. - No vendor lock-in. Nothing in
entries/is jotbook-specific. If you stop using jotbook tomorrow, you still have a folder of plain Markdown notes.
License
MIT.
