Overview
@bpmnkit/docspack ships the documentation you are reading as an npm package. An agent
installs it, asks a question, and gets back the two or three passages that answer it —
not a whole documentation site, and not whatever the model remembers about an older
release.
It follows the docspack package format, so the upstream
docspack CLI indexes it like any other vendor pack. The bundled bpmnkit-docs command
does the same job with no extra tooling.
- Offline —
ask,searchandlistread the filesystem only. No server, no network call, nothing resident between questions - Version-locked — the installed
package.jsonversion wins over the manifest, so an agent reads the docs for the release the project actually has - Bounded — three chunks and 3,000 tokens by default, budgeted from the manifest before any content is read
- Real retrieval — BM25 with Porter stemming, so
authenticatefinds a passage that only saysauthentication; API identifiers weigh 3× prose, and a page whose path tag is the query word (incidents) ranks above pages that only mention it - Zero runtime dependencies
The other pack
BPMN Kit publishes a second pack:
@bpmnkit/camunda-docspack, the Camunda 8
documentation — BPMN and FEEL references, engine concepts, best practices, the web
components, Self-Managed, the client guides and the Orchestration Cluster API. bpmnkit-docs reads both, so ask this package how to
drive the library and that one what the engine does:
npx bpmnkit-docs ask "how should I name an exclusive gateway" --pack @bpmnkit/camunda-docspack
Install them together, and tell your agent about both — see Using BPMN Kit with AI.
Installation
pnpm add -D @bpmnkit/docspack @bpmnkit/camunda-docspack
Giving an agent access
Any agent with a shell can run the command, so one paragraph in AGENTS.md, CLAUDE.md
or .cursor/rules is the whole setup:
Run `npx bpmnkit-docs ask "<question>"` for BPMN Kit documentation, and
`npx bpmnkit-docs ask "<question>" --pack @bpmnkit/camunda-docspack` for Camunda 8
documentation — BPMN semantics, FEEL, engine behaviour, the REST API. Both answer
from the versions this project installed. Prefer what they return over recalled
knowledge — when the two disagree, the retrieved chunk is right.
Asking a question
npx bpmnkit-docs ask "how do I deploy a process to Camunda 8"
## @bpmnkit/[email protected]/getting-started.quick-start.step-3-deploy-to-camunda-8
# Quick Start — Step 3: Deploy to Camunda 8
When you're ready for production, deploy to a real Camunda 8 cluster:
...
---
Source: /docs/getting-started/quick-start
---
cost: 1,204 tokens, capped at 3,000
Every answer names the package, the version and the chunk it came from, and closes with what it cost. An agent can quote the chunk id back when a passage turns out to be wrong.
Reading it yourself
search runs the same query and prints the ranking instead of the content:
npx bpmnkit-docs search "exclusive gateway condition"
5 result(s) for "exclusive gateway condition"
14.73 @bpmnkit/[email protected]/guides.gateways.exclusive-gateway-xor
Gateways & Branching — Exclusive Gateway (XOR) · 218 tokens
8.04 @bpmnkit/[email protected]/guides.gateways.inclusive-gateway-or
Gateways & Branching — Inclusive Gateway (OR) · 208 tokens
list shows which documentation packages were found and whether their manifests agree
with the versions installed.
Commands
| Command | Purpose |
|---|---|
bpmnkit-docs ask <question> | Answer from the installed docs packages — the command to give an agent |
bpmnkit-docs search <query> | Rank matching chunks, for reading in a terminal |
bpmnkit-docs list | Show the docs packages found and their index state |
bpmnkit-docs build | Regenerate the .llms/ payload from the docs source |
Options: --limit <n>, --max-tokens <n>, --pack <name>, --cwd <dir>.
--pack narrows before the index is built, not after, so asking one pack a
question does not pay for reading the others — roughly 150ms against 650ms
across both packs. A name that is not installed is an error listing what is,
rather than an empty answer that would read as “the documentation does not cover
this”.
Using the index directly
The package is also a library, so a bot, an editor extension or an MCP server can search without shelling out:
import { answer, discoverPacks, indexPacks } from "@bpmnkit/docspack";
const index = indexPacks(discoverPacks());
const { hits, tokens } = answer(index, "verify a worker's job type", {
limit: 3,
maxTokens: 3000,
});
for (const hit of hits) {
console.log(hit.chunkId, hit.content);
}
console.log(`${tokens} tokens`);
How the pack is built
bpmnkit-docs build reads the Markdown under apps/landing/src/content/docs, splits each
document at its ## headings, and writes one file per chunk:
packages/docspack/
├── package.json
├── llms.txt table of contents
└── .llms/
├── manifest.json chunk ids, token counts, tags, entities
└── chunks/
├── guides.gateways.exclusive-gateway-xor.md
└── ...
A section too large for the budget is subdivided at ### and then at paragraph
boundaries, never inside a fenced code block. A section too short to answer anything is
merged into the one before it, so a reference table does not become one chunk per row.
Tags come from front matter, the page’s slug and the heading’s own words; entities are
the qualified identifiers named in inline code.
You can steer either from the document itself:
## Two-column layout
<!-- docspack: tags=grid,columns -->
<!-- docspack: entities=TwoColumn -->
Trust
A manifest is third-party input that ends up in a model’s context, so it is read as a security boundary rather than as configuration:
- a chunk path that resolves outside
.llms/is refused - the installed
package.jsonversion supersedes the manifest’s, and a disagreement is reported bylist - chunk ids must be unique and must match
^[A-Za-z0-9][A-Za-z0-9._-]*$ - packages under the
@docspack-communityscope are labelled unreviewed in every answer