Skip to main content
A 6MB documentation corpus becomes ~1.5M tokens if attached whole — it blows any model’s window on the first turn. The knowledge mode of /context solves it with the same pull-first pattern as persistent memory: the conversation receives only an index card (what the base covers), and content is retrieved on demand — automatically each turn and, in the agent, iteratively via the @knowledge tool.

How it works

Native JSONL ingestion (docs-flatten)

Each JSONL line becomes a virtual document preserving source, title and provenance (repoUrl/commit) — instead of arriving as one opaque text blob. Malformed lines are counted and skipped, never fatal. Plain directories also become knowledge bases (normal scanner, up to 100MB). @docs-flatten accepts three sources for the same JSONL: root=<dir> (local folder), repo=<git-url> (shallow clone) and url=<site> (a bounded same-host crawl for docs that only exist as an HTML site, with no Markdown repo).

Code and infrastructure (kind=code)

Beyond documentation, @docs-flatten ingests source-code, Terraform and GitOps (Kubernetes/Argo) repositories — into the same JSONL schema, so knowledge mode changes nothing downstream. The kind parameter controls what enters and how it is sliced: The slicing is language-agnostic — it doesn’t rely on a per-language keyword list, so it doesn’t break when you switch stacks: The title is best-effort metadata: if the heuristic doesn’t recognize the language, it falls back to the cleaned signature line — content is always indexed and searchable, a missed title never costs recall. Noise is skipped by default (vendor/, node_modules/, .terraform/, lockfiles, minified assets, binaries) and files above 1 MiB are ignored.
With the three bases attached, @knowledge search fans out across all of them (each hit tagged by its source base), so the model connects the layers: “the checkout-api Rollout won’t go ready — connect the Argo manifest, the Terraform node group and the service health check in code”.
You do not need to classify the repo manually. The default is docs for safety, but the agent picks kind=code on its own: from intent (the tool schema documents the use), from the autonomous pipeline guidance (below), and from a self-correcting hint — running the default docs on a repo with no Markdown returns “looks like a code repo, re-run with kind=code, and it recovers in the same turn.

The index card (what enters the prompt)

Knowledge bases are also nodes in the knowledge graph (kbcontext kind, linked by their tags): @memory map counts them, @memory neighbors can walk into them, and they ride the persisted graph cache. See Bootstrap and Memory › Knowledge graph.
Attaching injects only a deterministic, budget-bounded TOC — name, scale, origin and the document list — living in the cached prompt prefix (byte-stable across turns). The model knows what exists without paying for the content:

Hybrid retrieval (keyless-first)

Each turn, the passages relevant to the question are injected into a volatile block (outside the cached prefix):
  • Pure-Go BM25 — always available, no API key, pt/English neutral. That’s the floor. The tokenizer splits snake_case, kebab-case and camelCase/PascalCase into sub-words (keeping the whole token), so an identifier like getUserName or aws_eks_cluster is found by user, eks, etc. — code recall without losing exact match.
  • Embeddings (Voyage/OpenAI/Bedrock, when configured) — semantic boost, fused by normalized ranking (0.55/0.45). An embedding failure degrades to lexical with a warning; it never breaks the turn.
  • Union, fused by rank — the vector index’s own top-k (over every passage embedded so far) joins the BM25 pool, and the union is fused by reciprocal rank fusion (ranks are comparable across corpora, so a single-hit corpus no longer scores 1.0 by construction); a passage that says it in other words surfaces even when BM25 has other results. The query is embedded once per turn and reused across every attached corpus; the LLM reranker sends its instruction once as the system message and books its usage as background spend.
  • BM25 floor — lexical hits under 5% of the best hit’s score are dropped from the candidate pool, so a query that barely matches the corpus returns a short, honest list instead of k passages of noise.
  • Optional rerank stage (CHATCLI_KNOWLEDGE_RERANK, default off) — reorders the head of the fused pool (up to 24 passages) before truncation. mmr is keyless: maximal marginal relevance over token overlap, so the passages returned cover different parts of the corpus instead of near-duplicates of the best hit. llm asks the compaction model (CHATCLI_COMPACT_MODEL, else the session client) for a listwise order, bounded by an 8 s timeout; any failure keeps the fused order.
  • Corpus weights/context attach <name> --weight 1.5 scales that base’s scores when hits from several attached bases are merged (1.0 is neutral). The weight is persisted with the session’s attachments.
  • Accent folding and light stemming (opt-in)CHATCLI_KNOWLEDGE_NORMALIZE=fold makes configuração and configuracao the same term; stem also folds common pt/en endings (configurações → configuracao, deploys → deploy, queries → query, rapidamente → rapida) while leaving short identifiers such as s3 or oauth2 untouched. Default off keeps ranking byte-exact; changing the mode reindexes the corpus (the mode is part of the index fingerprint), and @recall-style searches over the transcript use the same tokenizer.
  • Local, keyless embeddingsCHATCLI_EMBED_PROVIDER=ollama uses a local Ollama server (OLLAMA_HOST, default http://localhost:11434; model CHATCLI_EMBED_MODEL, default nomic-embed-text, dimension learned on first use) for the semantic boost with no API key and no cost.
  • Warm-up, not first-query latency — attaching a knowledge base (or --rag) and every refresh that changed something embed the missing passages in the background, in batches, so the first question after them does not pay the corpus embedding in the turn. Passage ids are content hashes: a refresh embeds only what changed. The vector index is written with an fsync’d atomic rename. Retrieval and warm-up share one bounded loop (64 passages per provider request) behind a per-context single-flight, so a question that lands during a warm-up waits for it instead of embedding the same ids twice. A batch the provider rejects is skipped and the rest still lands; the vector cache keeps every chunk that came back before a failure. Binary content (NUL bytes, invalid UTF-8) and minified bundles never reach the embedder, an over-long line is cut rune-safe at a hard cap, and truncated passages keep their citation line range true.
  • One budget per turn — the retrieved passages of all attached bases share a budget (~24 K characters, priority order); passages already emitted by a higher-priority base (same id, or the same content at the same place) are dropped, and the last base gets what is left rather than nothing.
  • Structured citations — every passage header ends with cite as [path:start-end] and the block asks the model to reference passages that way, so an answer can be traced back to the exact lines.
  • Embedding cost is visible — every embedding call is metered (tokens estimated from characters at the provider’s list rate) and shown by /cost as Embeddings: N call(s) · ~T tokens · $x; Ollama meters at $0.
  • Structure-aware passages — contexts created from now on cut passages at headings, blank lines and declarations (func, def, class, …) inside the last part of the window, and the boundary line opens the next passage. Existing contexts keep their fixed-window passages and their vectors; nothing is re-embedded behind your back.

The @knowledge tool — the agent investigates the base

In agent and coder modes, the index cards enter the system prompt and the @knowledge tool enables iterative investigation — search, read whole documents in pages, walk the structure: The use case that closes the loop — authoring skills from the docs with the @skill tool:

Autonomous pipeline — the agent builds the base itself (@context)

The steps above (flatten → create → attach) the agent does for you. When it hits a knowledge gap — a library, framework or API it doesn’t know — instead of guessing or stopping to ask, it builds the base itself:
1

Discover the source

@websearch for the official documentation (preferably the project’s Markdown repo), or use a repo/URL/path you pointed it at.
2

Flatten

@docs-flatten with root=<dir>, repo=<git> or url=<site> → produces the JSONL corpus. For a code/infra repo, it adds kind=code (one base per layer: app, infra, gitops).
3

Create and attach

@context create … --mode knowledge@context attach ….
4

Query

@knowledge search/get to ground the answer in the retrieved passages.
The @context tool gives the agent the same self-service power it already has for skills, but for knowledge: The tool mirrors the full /context surface, so the agent handles contexts end to end. The inspecting subcommands (list, status, show, inspect, metrics) are read-only. You stay in control: everything the agent attaches shows up in /context attached and @context status; remove it with /context detach or just ask (“detach the react docs”). attach auto-detects embeddings — knowledge mode uses keyless BM25 + vectors when configured, and reports which mode is active. In /agent the agent does all of this on its own; in /coder, state-changing operations go through the policy confirmation.
The @docs-flatten url mode is what closes the loop for docs that only exist as an HTML site (no Markdown repo): a bounded same-host crawl reusing the @webfetch fetch engine and emitting the same JSONL. Bounded by maxPages/maxDepth — no silent truncation.

In chat too (read-only exception)

Chat stays tool-less by design — but querying the knowledge base is the second sanctioned exception (next to ask_user), for the same reason: it executes nothing, only reads what you attached. Attach the base and talk normally; when the auto-retrieved passages aren’t enough, the model pulls more on its own (up to 4 pulls per turn: search → get → next page) before answering.
Works on the native tool path (API key) and the XML transport (OAuth providers) — like everything else, agnostic across the 14 providers.

Quick reference

Knowledge vs --rag: the existing /context attach --rag K is vector-only (requires an embedding provider) and only pushes per turn. knowledge mode works with no key at all, gives the model the corpus index and adds the pull side (@knowledge) — for documentation or code/infra corpora, prefer --mode knowledge.

Next steps

Persistent Contexts

RAG + HyDE

Skill Authoring

Bootstrap & Memory