TreeMap
무료 오픈소스 디스크 사용량 시각화 도구로, 사각형 트리맵·중복 파일 탐색·디스크 가득 예측·정리 시 파일을 휴지통으로만 이동한다
어떤 사이트 페이지에도 탑재되는 AI 채팅으로, 사용자가 만든 정적 인덱스에서 답변을 제공하고, 브라우저에서 검색·인용을 검증합니다
An AI chat that mounts on any page of any site and answers from a static index you build. Retrieval runs in the browser; an opt-in gate can refuse before the model is called; every citation is checked
생산성TypeScript · JavaScriptGitHub @Cloflin온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기An AI chat that mounts on any page of any site and answers from a static index you build. Retrieval runs in the browser; an opt-in gate can refuse before the model is called; every citation is checked against what was retrieved.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다.
README 전문 보기 ↗A grounded AI assistant for any page of any site — VitePress, Docusaurus, Vue, React, or a landing page with a <script> tag.
It is a chat, not a search box with better typography: scope a question to one section, ask about a passage you selected, follow up, edit what you asked, keep the thread. It mounts wherever a page can load a stylesheet and a script: a docs site, a landing page, a pricing page, a help centre, an app you already ship. What it answers from is the corpus you built — npx docpilot index walks your markdown and OpenAPI files, and npx docpilot import <url> pulls in an allowlisted page that lives in neither. A panel on your pricing page answers from that corpus, not from the pricing page.
Two installs, one panel. In the static install (runtime: 'browser', the default) retrieval runs in the reader's browser against an index built at deploy time — no vector database, no search service, no server beyond the one already serving your site. In the server install (runtime: 'server') the whole turn runs on your server and the page receives only the answer stream: the corpus, the instructions and the keys stay with you, behind rate limits, a turn deadline and a metrics endpoint. npx docpilot init asks which; the reader's chat is the same in both.
In the static install, retrieval runs in the reader's browser against a static index built at deploy time — no vector database, no search service, no server beyond the one already serving your site. It is hybrid: a BM25 pass over the chunk text and a cosine pass over the vectors, merged by Reciprocal Rank Fusion. The vectors are quantised at build time to one sign bit per dimension — 424 KB for this project's own 847-chunk index, where int8 would be 3.3 MB and float32 13.2 MB — scored against the question's full-precision vector, and the build measures the error and refuses to ship above 0.03 mean |Δcos|. A relevance floor on retrieval is scored on every turn and, opt-in (guard.mode: 'calibrated'), refuses before the model is called so an off-topic question costs zero tokens — off by default, because that threshold has to be calibrated per corpus and per language, and a reader's language is not something a threshold can see; the model's own citation contract is what handles it otherwise. Every citation the reader sees is checked against what the host actually retrieved during that turn.
| install | then | |
|---|---|---|
| npm | npm i @cloflin/docpilot | npx docpilot init |
| Yarn | yarn add @cloflin/docpilot | yarn docpilot init |
| pnpm | pnpm add @cloflin/docpilot | pnpm exec docpilot init |
| Bun | bun add @cloflin/docpilot | bunx docpilot init |
| Deno | deno add npm:@cloflin/docpilot | deno run -A npm:@cloflin/docpilot init |
pnpm exec docpilot is the form written here. A bare pnpm docpilot also works — pnpm falls back to pnpm exec for a name that is not a script — but pnpm run docpilot does not, and reports the bin as missing. Nothing here runs dlx or npx against the bare name — the unscoped docpilot is not this package, and nothing is published under that name today.
Node 20 or newer. Every command below is written npx docpilot …; substitute the runner from your own row — it is the same bin either way.
Any page that can load a stylesheet and a script — a docs site, a landing page, a help centre, an app you already ship. Which build of the panel you load is the only real question, and it is not which framework you use: it is whether your bundler compiles a .vue file, because that decides whether you get the source or a prebuilt bundle.
| your setup | entry point |
|---|---|
| VitePress | @cloflin/docpilot/theme |
| Docusaurus | @cloflin/docpilot/docusaurus |
| Vue | @cloflin/docpilot/vue |
| React | @cloflin/docpilot/react |
| any bundler | @cloflin/docpilot/mount or /web |
a <script> tag | dist/docpilot.web.js |
Every entry point ships TypeScript declarations. The full matrix is in the docs — see Documentation — and what follows is VitePress.
Two files and one environment variable. There are no settings to write.
// docs/.vitepress/config.mjs
import { defineConfig, loadEnv } from 'vitepress'
import { defineDocPilot } from '@cloflin/docpilot'
const ai = defineDocPilot({}, loadEnv('', process.cwd(), ''))
export default defineConfig({
vite: { plugins: [ai.plugin()] },
themeConfig: { docPilot: ai.themeConfig },
})
// docs/.vitepress/theme/index.js
import DefaultTheme from 'vitepress/theme'
import { withDocPilot } from '@cloflin/docpilot/theme'
export default withDocPilot(DefaultTheme)
echo 'OPENAI_API_KEY=sk-…' >> .env.local # or any one of fifteen — see below
npx docpilot index # build the retrieval index from your docs
npx docpilot calibrate # measure the refusal thresholds against your corpus
One key is the whole configuration. chat.provider ships as 'auto', which walks an ordered list of services and stops at the first one your environment holds a key for. That service's default model comes with it, and so does its embedder — so a single key covers both the answering and the retrieval half:
| embeds too | openai · gemini · mistral | together · fireworks · nebius | openrouter |
| answers only | anthropic · groq | deepseek · xai | cerebras |
| self-hosted (by address) | custom | llamacpp | ollama |
Providers that embed come first, and that is the ordering argument rather than a ranking of answer quality: the alternative needs a second key and posts the text of your whole corpus to a third party at build time. The self-hosted three have no credential to be found by, so OLLAMA_BASE_URL and LLAMACPP_BASE_URL select them and say where they are.
With nothing set at all, the chain falls through to OpenRouter's free tier — one free key, no model to choose on either half, no card. npx docpilot doctor prints the list and marks the member that answered.
To pin a provider, pass settings — and then export them by name:
export const docPilot = {
product: 'Acme Editor',
chat: { provider: 'openai', model: 'gpt-6-luna' },
}
const ai = defineDocPilot(docPilot, loadEnv('', process.cwd(), ''))
The docPilot named export is the contract between the build and the CLI: both read the same object, so there is no second place to state which model embeds or where the docs live. A provider you name is never overridden by the environment, and naming a provider without a model is a complete sentence — every one carries its own default.
product is optional. It is what the assistant says it answers questions about — in the instruction, in the composer placeholder, and when a reader says hello. Left out, all three read "this documentation".
vitepress dev proxies /ai/* for you; a built site does not. npx docpilot doctor --proxy prints the contract for the configuration you actually have: one route per hosted half — its exact path, its upstream, and the name of the variable holding the key, never the key — followed by the five rules those routes have to satisfy. The paths are asked of the adapter the browser will use rather than written out, so an Anthropic chat provider prints /ai/v1/messages; a setup that calls a local Ollama for both halves prints none needed.
The site still builds. The panel switches itself off and the build prints one block:
[docpilot] the panel is OFF — 2 things to set up:
· chat and embed: "openrouter" needs a key and none is set
export OPENROUTER_API_KEY=…
· no index at docs/public/rag
npx docpilot index
· chat.provider is 'auto' and no provider key was found in the environment,
so both halves fell through to openrouter — its free tier needs no model
named on either side and no card, so one free key finishes the install.
The site builds and every other feature is untouched.
Run `npx docpilot doctor` to re-check without a full build.
A dependency that can fail someone else's docs build the moment it lands is a dependency they remove. npx docpilot doctor is the opt-in place to turn the same facts into a non-zero exit for CI.
Four things are true of every answer, for every model, under every prompt — including a prompt you have edited. They are enforced by host code that no message can reach, and each is covered by a test.
It is a control against a weak, badly-behaved or injected model. It is not a security boundary and cannot be one: everything runs in the reader's browser, the corpus is a public website, and the model is one the reader could talk to directly. Scope is focus, not containment.
Three claims this README will not make. "It only answers from the documentation" — it answers with documentation in context, and generated text can contain anything the model knows. "Answers are grounded in their cited sources" — citation is provenance, not entailment. "It cannot be taken off-topic" — the gate is a relevance floor, not an entailment check, and a question overlapping a documented subject reaches the model by design.
Six doors open it — a question bar at the bottom centre (the default; a
floating button in the corner with ui.panel: 'popup'), the navbar button
beside your search, a row in the mobile menu, ⌘I (bound even with no
visible button), a call-to-action under each article, and open() on the handle
from mountDocPilot. One setting, ui.trigger, picks which of them exist.
Inside it:
| Scope | a picker in the composer narrows the search to this page, this section, or a hand-picked list — with a filter above the list once there are more than a dozen pages |
| Quote | select a passage and one button attaches it to the composer as a removable chip. On by default inside an answer, off over your own prose (quote.fromDocs) |
| Status | six named states rather than a spinner — loading the index, searching, looking at the page list, reading a page, thinking, writing — and reasoning collapses behind "Thought for 4s" |
| Citations | a Sources list, an optional disclosure opening the exact passage a marker was drawn from, and the sources appended to a copied answer |
| Refusals | the closest pages, what was searched, how many pages were read, and one button to clear the scope and search everything |
| The thread | edit a question and everything below it is discarded and answered again; ask again re-runs an answer that arrived; copy one turn, or the whole conversation as Markdown |
| Feedback | two thumbs, four reasons, and a 500-character comment redacted before it leaves the browser — posted to an endpoint you own, or nowhere at all |
| The prompt | prompt.show puts the assistant's own instruction, its tool definitions and the active scope in front of the reader, and prompt.allowAppend lets them add one of their own — sent with the question, never merged into the system prompt |
| Your tools | off by default. tools adds functions the model calls for live data — a plan's limits, an API's status — cited as a source "from the site", and actions the answer offers — a button or a form — that run only when the reader presses them |
Every one of them is a setting with a default and a reason, and all of them are in the docs under The assistant panel.
Algolia's Ask AI, kapa.ai, Inkeep, Mintlify's assistant and Orama are all real answers to this problem. One architectural decision separates them from this one: DocPilot does retrieval in the reader's browser against a static file you built, and every one of them does it on a server.
Quoted from the vendor's own pricing page, checked August 2026. not published
means there is nothing on the page to quote — not that it is free.
| published price | |
|---|---|
| DocPilot | $0 — MIT, no vendor, no plan, no per-answer fee |
| Algolia Ask AI | $0.50 per additional 1K search requests on Grow ($1.75 on Grow Plus), plus your own LLM key; Ask AI is not line-itemed on the pricing page |
| kapa.ai | not published — 14-day trial, then "Talk to us" |
| Inkeep | free, self-hosted; enterprise not published |
| Mintlify AI | ≈$0.23 an answer past the allowance — 23 credits at $0.01 over the 10,000 included; Starter has no AI |
| Orama | free, self-hosted (Apache 2.0); cloud price not published |
$0 is the software, not the answers. DocPilot bills nothing and has no
hosted half to bill from — no account, no service, no telemetry. You bring a
model provider key and pay them directly, or run the whole thing on OpenRouter's
free tier: one key, no card, and a ceiling of 50 requests a day that also
covers rebuilding the index. See
budget.
| DocPilot | hosted answer services | |
|---|---|---|
| Where retrieval runs | the reader's browser | the vendor's cloud |
| Where the index lives | a static file on the host already serving your site | the vendor's platform |
| Index the reader downloads | 1-bit vectors, one bit per dimension — 424 KB for this site's 847 chunks | n/a — the index is theirs |
| An off-topic question costs | zero model calls, zero tokens | not documented by any of them |
| Citations checked against what was retrieved | yes, by host code no message can reach | not documented by any of them |
| Refusal threshold measured on your corpus | npx docpilot calibrate | not documented by any of them |
| Self-hostable | there is nothing to host | Inkeep and Orama yes; the rest no |
Not documented is not the same as no. Where a vendor publishes nothing about a mechanism, both this table and the docs say so rather than guessing, and every non-DocPilot cell is sourced and dated on the comparison page.
The reasons to buy one of the others are on that page too, under What DocPilot is worse at — chiefly: it indexes what you can put in a directory, the reader downloads the index, nobody is tuning it for you, there is no analytics dashboard, and there is no support contract.
npx docpilot index | build the retrieval index from your markdown and OpenAPI files |
npx docpilot import <url> | turn an allowlisted external page into a page of the corpus |
npx docpilot calibrate | measure the refusal thresholds against your own corpus |
npx docpilot lint | check the golden set against the index it measures |
npx docpilot eval | run your golden set and write a report |
npx docpilot bench | A/B two retrieval configurations on answer quality, with no API key |
npx docpilot tune | sweep the retrieval levers (lambda, k) against the golden set into docpilot/tuning.json, with a report of the grid beside it |
npx docpilot feedback | turn readers' votes into candidates for the eval sets |
npx docpilot doctor | check the configuration without a full build; exits non-zero when not ready |
npx docpilot init | scaffold the environment, the eval sets and the authoring skills; asks which agent tool gets them |
npx docpilot update | refresh those copied skills and the /docpilot-* slash commands after an upgrade |
npx docpilot serve | run the turn on your own server (runtime: 'server'), optionally beside the built site, with a turn log |
npx docpilot traffic | read the turn log: content gaps, vocabulary misses, pages never cited, golden-set candidates |
npx docpilot dataset | build SFT, KTO or DPO training sets from the turn log and readers' votes |
npx docpilot model | fingerprint a checkpoint, check a training set for eval leakage, compare two eval runs |
npx docpilot llms | lint an llms.txt against the format and your index, or build one from the manifest |
npx docpilot improve | turn a report's failures into the edits worth making; judge an edit before and after; guard it against copied test answers |
npx docpilot with no command, in a terminal, opens the home screen: the status and a menu of the next steps. eval measures either install — --runtime=server runs the server turn itself — on one half of the golden set (--split=dev|test), or runs the security probes (--set=adversarial). init also installs two Claude Code agents, docpilot-tuner and docpilot-writer, that run the improvement loop on your docs locally: edits chosen on the dev half, kept only when the test half agrees.
eval, bench and tune each take a --level=low|medium|high|xhigh|max|ultra, and the six tiers are cumulative: --level=medium runs low + medium, no --level runs everything. Two reports are comparable only within one tier.
The loop is index → calibrate → lint → eval → bench, with tune where it is retrieval that has to move — and then index again, because that is the step that inlines tuning.json into the manifest a reader downloads. Until it runs, a swept lever is a file on disk and nothing more. The first two make the panel work; the last three tell you whether it works well, and they are the half that gets skipped.
feedback sits outside the loop: it reads what your own endpoint collected and proposes probes for it. It never writes to the eval sets — a stratum is a judgement and a gold answer is written by a person.
Calibrate before you ship. Thresholds are a statement about one corpus and do not transfer between projects. Until calibrate has run, the gate uses provisional values and every record says so.
One provider is usually enough. embed: 'auto' follows chat.provider and uses that service's own embedding model.
Chat and embeddings: openai, together, fireworks, mistral, nebius, gemini, openrouter, ollama, custom.
Chat only — no embeddings endpoint: anthropic, deepseek, groq, xai, cerebras.
Choosing one of them with embed: 'auto' borrows OpenRouter's free embedding pool for the retrieval half: set OPENROUTER_API_KEY, and npx docpilot index picks whichever free embedder answers. The borrow is printed rather than assumed — the text of every chunk is posted to OpenRouter at build time, so name embed explicitly if your corpus may not leave for a third party.
chat: { provider: 'openrouter' } with no model named runs both halves on the free tier: chat rotates through a pool of free models per call, and npx docpilot index picks whichever free embedder answers and records it in the manifest. That tier meters requests, not tokens — 50 a day under 10 credits bought — and one turn is an embedding request plus two or three model calls, so fifty is a dozen-odd questions rather than fifty. What the panel does about that, and what you have to decide, is budget.
embed: false runs no embedder at all: the index carries no vectors and every question is retrieved by keyword. It is supported, and it costs most of the recall — measured on a 1191-chunk corpus, recall@8 fell from 0.97 to 0.41, retrieval F1 from 0.35 to 0.18, and 11 of 44 answerable questions were refused outright. Keyword matching also scores zero for a question asked in a language your corpus is not written in. Measure your own corpus with npx docpilot eval --gate-only --lexical before choosing it — it reads the golden set you wrote, so that comes first.
chat: false runs no model at all — search-only. A question is scored against the index exactly as before and answered with the ranked passages themselves: each one an excerpt under a link to the heading it was cut from. The scope picker, the credential check and the calibrated gate all still run; what changes is that nothing is generated, so nothing can be wrong and no request is made. Paired with embed: false it is a deployment that holds no provider key and makes no outbound request of any kind after the page loads.
See Choosing providers.
The panel refuses a question containing a credential shape — API keys, JWTs, bearer tokens, AWS key ids, hex digests — before the embedding call, so the value never leaves the browser. It is replaced with a placeholder, the warning is written in the reader's language, and one button answers the original question without it.
This is a habit guard, not a security boundary. It cannot stop a reader who pastes a key into a model directly.
"Hello" carries no documented subject, so the gate scores it at zero and refuses it — a correct verdict that tells the reader, on their very first message, that the assistant is broken. Greetings, thank-yous, farewells and "who are you" are recognised before the gate and answered from a template in eighteen languages, with no model call. A greeting attached to a real question is not claimed.
Conversations are kept in the reader's own localStorage and listed in the panel, so a reload — or a citation followed into a new tab — no longer throws a thread away. The archive is shared by every tab; which conversation a tab is showing is not, so two tabs are two conversations.
Reasoning, retrieved excerpts and the reader's own instruction are never written. Nothing is sent anywhere. The reader can delete one conversation or all of them, and history: { enabled: false } stops recording and clears what is stored.
npx docpilot import <url> turns an allowlisted external page into a page of the corpus. If the site already publishes page.md beside page — declared as an alternate, or derived from its canonical URL — that file is the import, because it is what the page was built from. Otherwise the markup is converted to markdown in code, never summarised by a model. Either way, one final model pass may add <llm-only> / <llm-exclude> and nothing else, verified by comparing its output to its input character for character.
Point importDir at a directory outside your docs and the assistant will answer from pages that have no route on your site — a product page, a policy — and cite the original. Every source: is checked against sources.allow, https-only, at build time: that value becomes an href in the answer panel, so markdown is never trusted with a URL scheme.
Every reader-facing string — 185 of them, in 26 groups — is replaceable one at a time, in the same shape as VitePress's own local-search i18n:
i18n: {
locales: { ru: { translations: { empty: { heading: 'Чем помочь?' } } } },
}
Panel chrome follows the page's locale; the credential and greeting replies follow the language the reader typed. A key that does not exist is dropped and named on stdout.
npx docpilot init asks which agent tool you use — Claude Code, Codex, Cursor, Copilot — and copies two skills into it: docs-rag, the measurement and tuning loop with a list of experiments already run and what they cost, and docs-import, the contract for imported pages. It also generates a /docpilot-<command> slash command for every command of this CLI. A skill inside node_modules reaches nobody, so copying is the only delivery there is.
npx docpilot update refreshes both after you upgrade the package. A file you edited is replaced and your version kept beside it as .bak.
Full documentation — the assistant panel, feature by feature, how it compares to the hosted services, the install matrix for every host, the configuration reference, the pluggable highlighter API (Shiki, Prism or highlight.js, none of them installed by default), the retrieval contract, translation, imported pages, and deploying it (nginx, containers, cache rules for the index): run npm run docs:dev in this repository.
MIT
npx docpilot status | one picture of the project: index freshness, thresholds, eval sets, the last report with deltas, the server, the next step |
npx docpilot monitor | live metrics of a running server: turns, latency, first token, outcomes, cache, tokens |