Skip to content

devp0nt/doc0

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

🚧 Not ready — please don't use this yet. doc0 is an experiment: the API and the whole usage design are still being explored, the implementation is rough, and anything here can change or break without notice. It's public only to develop in the open and reserve the name. Please don't depend on it in anything real.

@devp0nt/doc0

One navigable, searchable collection from the docs you already write.

CI npm coverage gzip license

You — and, more and more, your agents — write docs all the time: JSDoc, code comments, Markdown. But they're write-only: scattered across the repo, unsearchable, and forgotten the moment they're saved. As agents write more of the code, the hard part stops being typing and becomes staying on top of a codebase you didn't hand-write.

doc0 turns the docs you already write into one live, navigable, searchable collection, and serves it to everyone who needs it: to you in a local web UI and from the CLI, to your agents over MCP, to your build as generated files. No new format — it reads the JSDoc and Markdown you already have.

So it's not a doc-site generator, and not an agent-rules file. It's the layer that keeps you in command of your own project: every convention, decision, and gotcha is one query away — by name, tag, or meaning, and always pointing at the exact file:line — even when an agent wrote the code. You write a doc once; doc0 keeps it findable, linked, and fresh.

// docs/index.ts — point doc0 at the docs you already write
import { Doc0 } from '@devp0nt/doc0'

export const doc0 = Doc0.create({
  glob: ['src/**/*.ts', 'docs/**/*.md', '!**/*.test.ts'],
})

Any JSDoc with a doc0 directive becomes a doc — id, tags, and a @related graph, all from a comment you'd write anyway:

/**
 * Money is stored in minor units (cents). Never do currency math in the UI —
 * format through here.
 *
 * @id money
 * @tags rule, money
 * @related cart-total
 */
export const formatMoney = (minor: number): string =>
  `$${(minor / 100).toFixed(2)}`
bunx doc0 web    # you: browse + search every doc in the project
bunx doc0 mcp    # your agents: list_docs / search_docs / get_doc — the same collection

That money rule you wrote once is now one query away — for you in the browser, for your agent over MCP — both landing on src/utils/money.ts.

Install

doc0 is a dev tool — add it as a dev dependency:

bun add -d @devp0nt/doc0
# or: npm i -D / pnpm add -D / yarn add -D

The web UI, MCP, keyword search, and export work out of the box — nothing else to install. Only semantic search is opt-in (it pulls a local ML model, hundreds of MB):

bun add -d @huggingface/transformers

Bun 1+ or Node.js 20+. ESM only.

Point doc0 at your docs

One config module exports the engine. Runners (the CLI, the MCP/web servers) load it — the config itself runs nothing.

// docs/index.ts
import { Doc0 } from '@devp0nt/doc0'

export const doc0 = Doc0.create({
  // code files: a JSDoc joins the collection when it carries a doc0 directive.
  // markdown files: every match joins, unless its frontmatter says `doc: false`.
  glob: ['src/**/*.ts', 'docs/**/*.md', 'README.md', '!**/*.test.ts'],
  // map a category id to a human title (drives grouping in the web UI and export)
  category: { rule: 'Rules', util: 'Utilities' },
})

A doc has a small, fixed shape: id, title, description, tags, category, related, content, and source (file + line span). Missing fields are filled from the content — title from the first heading, id from the symbol or file name — so a bare ## Heading Markdown file is already a valid doc. Write a comment for the why and the gotchas; doc0 turns it into something the whole project can find.

Own your codebase — even when agents write it

This is the point doc0 exists for. When agents type most of the code, your job shifts to steering and reviewing, and that needs the knowledge that keeps a project coherent close at hand — the why, the "use X not Y", the canonical helper. doc0 makes that knowledge a living map you drive from:

  • browse the whole project's docs in a local web UI;
  • search by keyword, or by meaning;
  • follow the @related graph from any doc to its neighbours;
  • jump straight to the file:line a doc lives at.
bunx doc0 web                              # http://localhost:4000 — sidebar, search, rendered markdown
bunx doc0 search "how do we format prices" # => money, cart-total, …
bunx doc0 get money                        # full content + source + related, in the terminal

Search works out of the box (orama keyword/BM25). For "find by meaning", turn on embeddings — the small model downloads once, runs locally, no API key:

import { oramaSearch } from '@devp0nt/doc0/search'

export const doc0 = Doc0.create({
  glob: ['src/**/*.ts', 'docs/**/*.md'],
  search: oramaSearch({ embeddings: true }), // hybrid keyword + semantic
})

Write a doc once and it stops rotting: it's indexed, linked, and always one query away — so you stay in tune with a codebase that's growing faster than you can read it line by line.

Your agents read the same docs

Your agents query the very same collection — so they follow your conventions instead of re-deriving (and sometimes mis-guessing) them. No parallel rules file that drifts from the code; one source you both read.

Point an agent at it with a single instruction:

<!-- AGENTS.md -->

Before any task: `list_docs({ tag: "rule" })`, read the rules that apply, and
`get_doc` them. Search the rest with `search_docs("…")` before guessing.
bunx doc0 mcp

The MCP server exposes a small, fixed tool set any agent understands — it scales to a project with hundreds of docs, where one-tool-per-doc would not:

Tool What it returns
list_docs table of contents — paged, filter by tag / category
search_docs top matches with snippets
get_doc one doc in full, with source and an enriched related list

source carries the file path and line span, so an agent jumps from a rule straight to the code it governs — the same way you do.

Generate and export

The collection is also data you can emit. Generate artifacts (Cursor rule files, a JSON index, anything) — written only when content changes — or dump the whole thing to one file for a hand-off, a PR, or another tool:

export const doc0 = Doc0.create({
  glob: ['src/**/*.ts', 'docs/**/*.md'],
  generate: (doc) =>
    doc.tags.includes('rule')
      ? { path: `.cursor/rules/${doc.id}.mdc`, content: doc.content }
      : undefined,
  finalGenerate: (docs) => ({
    path: 'docs/source.json',
    content: docs.toJSON(),
  }),
})
bunx doc0 sync                          # parse → run generators (diff-only)
bunx doc0 export --format md > docs.md   # one Markdown bundle: TOC + every doc

collect() revalidates itself (it re-scans changed files by mtime), so the MCP and web servers never serve stale docs — no watcher needed for reads.

Reference

CLI

doc0 list   [path]   # table of contents — --tag --category --fields --limit --offset --json
doc0 get    <id>     # one doc in full — --fields --json
doc0 search <query>  # keyword + semantic — --limit --fields --json
doc0 export [path]   # whole collection → file — --format md|json --tag --category -o
doc0 sync   [path]   # run generators (diff-only) — --watch
doc0 mcp    [path]   # serve over MCP (stdio)
doc0 web    [path]   # serve the web UI — --port
doc0 prune  [path]   # drop the on-disk cache

path is optional — defaults to docs/index.ts (or doc0.config.ts), or pass any path to the config module.

Directives

In a JSDoc comment (any one of these makes the comment a doc), or in Markdown frontmatter:

Directive Meaning
@id stable id (else the symbol or file name)
@title title (else the first heading, else a humanized id)
@description one-line summary (else the first content line)
@tags comma list; rule is just a tag
@category grouping tag(s) — drive folders and the web sidebar
@related linked ids; a trailing ! (e.g. @related setup!) means must-read
@doc force-include a comment that has no other directive

Markdown joins by glob; opt a file out with doc: false in its frontmatter.

Packages

Import What
@devp0nt/doc0 Doc0 / Docs, parsers, collect / sync / watch, export
@devp0nt/doc0/parsers the built-in parsers + the Parser contract for your own
@devp0nt/doc0/mcp serveMcp(src) — the MCP server
@devp0nt/doc0/search oramaSearch() — keyword by default, semantic opt-in
@devp0nt/doc0/web serveWeb(src) — the web server + UI

Conventions

How to author docs doc0 collects well — what earns a doc, the rule tag, category areas, @related — lives in docs/conventions.md. Copy it into your project as docs/docs.md and tweak the area list.

A runnable end-to-end project lives in examples/minimal.

Requirements

  • Bun 1+ or Node.js 20+ (ESM only)
  • TypeScript 5+ (optional — works in plain JS too)

Community

Questions, bugs, or want to hang with other builders? Join the devp0nt community — one hub for all our open-source projects, this one included. Get help, share what you built, or just say hi: p0nt.dev/community

Contributing

Issues and PRs welcome. See CONTRIBUTING.md and the Code of Conduct. Commits follow Conventional Commits. Security reports: SECURITY.md.

License

MIT


Building open-source software for the glory of the Lord Jesus Christ ☦️
With love for developers of all backgrounds around the world ❤️
Sergei Dmitriev, 2026 😎

About

Turn the docs you already write — JSDoc, comments, Markdown — into one navigable, searchable collection served over MCP, a web UI, and generated files.

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors