docspack v1.2.0

one source two readers no server

Documentation as an npm dependency. Agents read it. People read the same thing.

A library publishes its docs as a package. You install it beside the code, and one command answers from the version you actually installed — offline, without a page in context.

one markdown source

docs/
webhooks.md
auth.md
migrating.md

The Markdown the library already writes. Parsed once, then emitted as both of these.

for the agent · installed

$ npx docspack ask "verify a webhook"
## @acme/docspack@1.4.0
Verify the signature header on every event.
version
1.4.0 — matches lockfile
cost
2,140 tokens, capped at 3,000

for the person · published

acme.dev/docs/verify#drift — cited by the answer

Install it  → Why not MCP or a skill?
$ pnpm add -D @acme/docspack

the gap

Three ways to answer one question

You have @acme/docspack@1.4.0 in your lockfile. The agent asks how to verify a webhook. Only one path is bound to the version you are running.

01 · training data
Recalled from weights
answers for 0.9.2

Frozen. The signature it remembers quietly stopped existing.

02 · fetch the docs site
Network round trip
answers for 2.1.0

Today's truth. Whatever the vendor publishes now — not what you installed.

03 · docspack
Local index, keyed to the lockfile
answers for 1.4.0

Correct by construction. The docs were installed with the code.

how it works

Three moves, no server

01

Vendors publish docs to npm

A docs package is an ordinary npm package: Markdown split into chunks, plus a manifest describing them. Its version tracks the library's.

// package.json
"devDependencies": {
  "@acme/docspack": "1.4.0"
}
02

docspack indexes them locally

One sync walks the docs packages this project depends on and writes their chunks into one SQLite database per machine. Keyed by name and version, so a release is read once no matter how many projects use it. It reads each installed library's own type declarations too, so a name nobody wrote a page about still has an answer.

$ npx docspack sync
+ @acme/docspack@1.4.0  128 chunks  indexed
+ hono@4.13.3           272 chunks  indexed (declarations)
= @other/docspack@0.0.8   44 chunks  cached
03

Agents ask the index

Every agent already has a shell, so the setup is done: no server to spawn, no per-client configuration, nothing resident when nobody is asking. One command writes the instruction into the AGENTS.md or CLAUDE.md this project already keeps — plus a Claude Code skill when there is one — and a second fails CI once that wiring drifts from what the tool does.

$ npx docspack agent install
+ AGENTS.md
+ .claude/skills/docspack/SKILL.md

# in CI, so a pasted line cannot go stale
$ npx docspack agent check
ok AGENTS.md
101ms

Per query, of which 41ms is Node starting and 60ms is docspack. Invisible next to the seconds an agent spends thinking.

SQLite FTS5

Porter stemming, so authenticate finds a chunk that only says authentication.

Also MCP

docspack mcp serves the same index over the protocol, byte-identical answers, if you would rather have a tool.

instead of

Why not the thing you already have

The alternatives are not less capable. They differ on one axis: which version of the truth reaches the model, and how much of it.

How docspack compares with a docs site, an MCP server that fetches, and an agent skill, across six properties.
docspack Docs site MCP server Agent skill
Matches your lockfile Always Latest only Latest only Authored once
Works offline Yes No No Yes
Resident in context Never Pasted pages Tool defs, always Whole skill, always
Names the docs never mention From the installed build Absent Absent Absent
Cost of one answer ≤ 3,000 tokens Whole page Whole page + trip Whole document
Setup per project One command None Per client Per team

docspack

Matches your lockfile
Always
Works offline
Yes
Resident in context
Never
Names the docs never mention
From the installed build
Cost of one answer
≤ 3,000 tokens
Setup per project
One command

Docs site

Matches your lockfile
Latest only
Works offline
No
Resident in context
Pasted pages
Names the docs never mention
Absent
Cost of one answer
Whole page
Setup per project
None

MCP server

Matches your lockfile
Latest only
Works offline
No
Resident in context
Tool defs, always
Names the docs never mention
Absent
Cost of one answer
Whole page + trip
Setup per project
Per client

Agent skill

Matches your lockfile
Authored once
Works offline
Yes
Resident in context
Whole skill, always
Names the docs never mention
Absent
Cost of one answer
Whole document
Setup per project
Per team

A docs MCP server that fetches

Closest relative, and no reason it cannot exist — docspack mcp is exactly that. The difference is what sits behind it: a fetching server returns the same text to every project, pays the network on each call, and ranks whole pages because there is no local index.

An agent skill

A skill teaches an agent how to do something — your review checklist, your deploy runbook. It is authored once and loaded whole. Dependency docs change when you bump a version, and nobody wants to hand-edit them. Skills and docspack compose: the skill says “check the docs first”.

Vendoring docs into the repo

Copying Markdown into CLAUDE.md is the right instinct — it just does not survive upgrades. Nothing notices when it goes stale, and a flat folder has no index, so the agent reads the whole file to find one line.

A vector database

Better recall for vague questions, at the price of an embedding pipeline, a service, and a re-ingestion story on every change. Aimed at a corpus you do not have. Yours is small, textual, and already versioned by the package manager.

measured

What an answer actually costs

Against 815 chunks of one vendor's published documentation — 155,056 tokens, mirrored from their llms.txt. Every figure here is generated from one measurement run, not typed in.

1.1%

of the documentation reaches the model, at most — and a response is capped at 3,000 tokens no matter what was asked.

1,711

tokens for the largest answer measured. A warm CDN returns 34,546 tokens of HTML to answer the same question.

123ms

with fetch, DNS and TCP removed from the process. Not configured off — removed, so any attempt to reach out would throw.

51% / 23%

of what hono and zod export is mentioned anywhere in their own documentation. The rest is declared in the installed build.

The full measurement Per-query token budgets, P50/P95 timings, and the coverage gap in hono and zod

In context

Everything published, in one file
155,056
ask "validate a request body"
1,711
ask "how do I set a cookie"
939
ask "streaming responses"
427
ask "verify a jwt"
508

In time

Median and 95th-percentile durations for each step of answering one query, and for one fetch of a documentation page.
Step p50 p95 samples
Node starting the runtime, before docspack runs at all 40.65ms 49.95ms 60
docspack ask, end to end a whole process, spawn to answer 100.81ms 113.41ms 60
The query itself discovery, open, rank and budget, in process 0.54ms 0.85ms 240
The full-text search ranking 815 chunks 0.51ms 0.84ms 240
One documentation page, fetched one warm CDN-cached page, measured from the benchmark host 48ms 63ms 20

Half the API is documented nowhere

A documentation site is written page by page, around tasks. An API grows name by name. The two never converge, and nothing measures the difference — so we did, against two projects that document themselves well. Every uncovered name is declared in the package already in node_modules, which is where docspack is already running.

hono@4.13.3

Names it exports
367
Mentioned in its docs
189
Undocumented, declared
154

zod@4.4.3

Names it exports
1,370
Mentioned in its docs
309
Undocumented, declared
1,041

Intel(R) Xeon(R) Processor @ 2.80GHz · 4 cores · linux x64 · Node v22.22.2 · docspack 0.4.0 · 2026-08-22 · pnpm --filter @docspack/bench perf reproduces it · the raw measurement
hono@4.13.3 · zod@4.4.3 · 2026-09-02 · pnpm --filter @docspack/bench coverage reproduces it · the raw measurement

your own sources

The same mechanism, over material nobody published

A context window is exhausted just as easily by a project's own material — notes, decision records, an export, rows out of a query. docspack index indexes what you already have, and docspack recall answers from it. No driver, no server, no embedding step.

Whether it beats letting an agent grep the files

Measured, not assumed — on 269 chunks of prose nobody wrote for retrieval, against 17 questions fixed before the first run. Reading everything grep suggests is the more accurate arm. It costs about 14× the tokens.

The index, top 3
15/17 · 1,172
grep, a window around the best hit
8/17 · 1,035
grep, its top three files read whole
17/17 · 16,074

269 chunks · ~93,518 tokens · 17 held-out questions · commit c221778

the format

A docs package is just files

Any tool can read one. The manifest maps chunks to search terms, so indexing needs no model in the loop. Every field is written down in the package specification, and the rest of the manual is in the documentation.

node_modules/@acme/docspack/
├─ package.json  holds the version
├─ llms.txt      table of contents
└─ .llms/
   ├─ manifest.json chunk routing table
   ├─ chunks/
   │  ├─ api-auth.md
   │  └─ webhooks.md
   └─ schemas/      optional openapi
{
  "name": "@acme/docspack",
  "version": "1.4.0",
  "chunks": [
    {
      "id": "api-auth",
      "tokens": 150,
      "tags": ["authentication", "bearer"]
    }
  ]
}

Generate one from what you already have

Split Markdown at headings, or walk an OpenAPI document. The only command that touches the network reads a project's public llms.txt.

$ npx docspack build --from ./docs
$ npx docspack build --openapi ./api.json
$ npx docspack build hono

publish docs · sheaf

One source. A site for people, an index for agents.

docspack answers an agent from documentation somebody published. Sheaf is where that documentation becomes the site a person reads. The same Markdown is parsed once into a content graph, and both the pages and a docspack package can be emitted from it, so the two cannot describe different documentation. This site is built that way: the manual you are one click from is rendered from the graph.

It mounts, it does not generate

A docs framework usually hands you an app and one exit. Sheaf hands you a graph and components: render them in the Astro site, React app or Preact app you already have, at whatever path you already own. There is no generated project, so there is nothing to eject from.

An answer that links back

A graph knows where its pages are published, so a chunk cites the URL and anchor it came from rather than a filename. The agent's answer carries a link a person can open — which is the part a folder of Markdown cannot know.

Four packages, and you take the ones you need

sheaf

Sheaf's content graph: a directory of Markdown as one serializable object.

sheaf-astro

Renders a Sheaf graph with Astro's own Markdown pipeline.

sheaf-react

The Sheaf docs shell — sidebar, page, contents and pager, built on cascivo.

sheaf-emit

Turns a Sheaf graph into a docspack package, so one source serves both audiences.

// one directory of Markdown, parsed once
const graph = await buildGraph("./docs");

// → the page a person reads
const { html, headings } = await renderPage(page);

// → the package their agent answers from
await emitDocspackPackage(graph, {
  baseUrl: "https://acme.dev/docs",
});

// which is what puts a link in the answer
<!-- docspack: from https://acme.dev/docs/verify#drift -->

Early — v0.1.0, and versioned accordingly · how the two halves fit · npm · source

feedback

The agent noticed the docs are wrong

An agent has the documentation, the installed library and a failing program in front of it at the same moment. That signal is worth capturing, and today it evaporates.

guarantees

What holds, every query

Offline by construction

sync, ask, search and list make no network requests. They read node_modules and a local SQLite file.

Version-scoped answers

The shared index may hold five versions of a library. A project only ever sees the one it installed.

Bounded responses

Three chunks and 3,000 tokens by default, counted from the manifest before content is returned.

Untrusted input, treated as such

Community packages are labelled, and a manifest path that escapes the package is refused rather than read.

questions

Before you install it

What is docspack?

docspack distributes a library's documentation as an ordinary npm package, so the docs install alongside the code and stay pinned to the same version. It indexes those packages into a local SQLite database and answers questions from it with one command, offline.

Do I need to run a server?

No. docspack is a CLI: the agent runs docspack ask in the shell it already has. There is nothing resident when nobody is asking. An MCP server ships too (docspack mcp) and serves the same index, for assistants that cannot run commands.

What if a library does not publish a docs package?

Build one yourself with docspack build --from ./docs, from an OpenAPI document, or from a project's public llms.txt. The result is a normal npm package you can publish or keep private.

What if the library's own documentation never mentions it?

That is the common case: measured against two projects that document themselves well, their own documentation mentions about half of what they export. docspack sync also indexes each installed library's type declarations, so an answer about an undocumented name leads with the declaration from the build you installed and says the documentation does not cover it. Declarations are matched by name and never ranked against prose, and docspack sync --no-artifacts turns them off.

How much context does an answer consume?

Three chunks and 3,000 tokens by default. The cap is counted from the manifest before any content is returned, so a query cannot overrun it. Measured against one vendor's published docs, at most 1.1% of the corpus reaches the model.

Does docspack send anything anywhere?

No. sync, ask, search and list make no network requests at all, and docspack contains no code that transmits a feedback report. The only command that touches the network is docspack build, which reads a project's public llms.txt when you ask it to.

Three commands, nothing to paste.

MIT. Requires Node 22.5.0+. Currently v1.2.0. The package format is specified, and breaking it is a major release.

pnpm
$ pnpm add -D @acme/docspack
$ pnpm dlx docspack sync
$ pnpm dlx docspack agent install

The first installs the docs beside the code. The second indexes them. The third tells your agent the index exists — and docspack agent check fails CI if that wiring ever drifts.

The same ranking docspack search uses, over the same 13 documents.