Introduction
The problem promptbook solves, the fix, and your first 60 seconds.
The problem
System prompts start as throwaway text. Then they get reused across flows, grow
if (model === "gpt") branches, and end up scattered through the codebase as
template literals. At that point you can't answer three basic questions:
- Which pieces are shared between prompts?
- What is safe to change without breaking another flow?
- What does the model actually receive under a given context?
So editing is scary, and reviewing a prompt PR means eyeballing a string blob.
The fix
promptbook turns prompts into a folder of small plain files, split into three boring layers:
- WHAT · reusable Markdown fragments with a YAML frontmatter id.
- WHEN · declarative YAML rules that say which fragments apply for which context.
- HOW ·
resolve()returns a{ text, trace }pair you then send to a model.
Same folder + same context → byte-identical string, every time. The trace
explains why: which rules fired, what got replaced or forbidden, and every
warning. Nothing throws on bad data; holes surface as warnings instead.
Your first 60 seconds
No install needed. Try the in-browser demo right now — it is the real viewer running the real resolver. Or run it locally:
npx skills add markbrutx/promptbook # teach your agent
npx @markbrutx/promptbook-cli init # scaffold a starter book in ./prompts
npx @markbrutx/promptbook-cli view # open it: canvas + graph
npm install @markbrutx/promptbook-core # use resolve() in codeinit writes promptbook.json plus five fragments and one composition whose
rule swaps the tone fragment on tone=terse — a working cascade you can
resolve, lint, and view immediately. It never overwrites existing files.
Any one of the three is a complete entry point. The agent skills include
promptbook-migrate, which moves your existing scattered prompts into a book
without changing what the model receives.
Not a framework
Adopting promptbook does not restructure your app:
- No orchestration. No chain helper, no retry loop, no streaming client. The unit of work is one assembled prompt string; what you do with it is your code.
- No model calls in the engine.
resolve(),lint(), and loading are pure and deterministic. The only stochastic step — calling a model — lives behind an adapter you control, and onlyevaluses it. - No DSL beyond the rule shape. Rules have four actions (
add,replace,forbid,order) and awhenclause. That is the entire surface. - No lock-in. Your prompts are Markdown and YAML on disk. Delete the tool and the files still read as plain text. The target model is just another context axis, so no provider is privileged.
The runtime stays dumb on purpose; the intelligence lives in the agent that drafts your fragments and rules.