pi-kb — pi-native Knowledge Base
A pi extension that compiles markdown documents into a structured, interlinked wiki using your LLM. Inspired by OpenKB (which also inspired by Andrej Karparthy) but built entirely as a pi extension with no external dependencies beyond your LLM.
Install
pi install git:github.com/dheerapat/pi-kb
Usage
/kb-init <name> Create a named workspace
/kb-add [-f] @file | url Add a markdown file (via @) or URL; -f skips pending-confirmation
/kb-add-content <text> Add inline markdown text (LLM chooses the title)
/kb-query <question> Ask a question against the knowledge base
/kb-list List all documents and concepts
/kb-status Show knowledge base stats
/kb-remove <docName> [-y] Remove a document and clean up wiki pages; -y skips confirmation
/kb-repair [docName] Re-compile interrupted /kb-add documents
/kb-clear [-y] <name> Clear workspace content (keeps directory); -y skips confirmation
/kb-ws-rm [-y] <name> Delete a workspace; -y skips confirmation
/kb-workspaces List all workspaces and their stats
All commands accept -w <name> to target a specific workspace:
/kb-add -w myproject docs/design.md
/kb-query -w myproject "what is the caching strategy?"
/kb-list -w myproject
/kb-status -w myproject
/kb-remove -w myproject design
If no workspace is specified, commands operate on the default workspace at ~/.pi/agent/kb/.
Interrupted compilations & recovery
When you run /kb-add, the source file is copied and registered immediately,
but the wiki compilation (summary, concepts, index) runs asynchronously through
the LLM. If the session is interrupted mid-compilation, the registry will list
the document but the wiki will be incomplete.
At most one document can be pending at a time. If you try to /kb-add a new
document while another is still pending, pi shows a confirmation dialog: discard
the pending document and add the new one, or keep the pending one (use
/kb-repair to finish it). Pass -f to skip the dialog and force-discard:
/kb-add -f new-file.md. Re-adding the same file or URL while it's pending
triggers a re-compile — the dedup logic runs before the guard, so retrying the
same document always works.
Detection: /kb-status shows a ⚠ Pending compilation line. /kb-list marks
them with ⚠[pending].
Recovery: Run /kb-repair to re-compile all pending documents:
/kb-repair # Re-compile all pending docs
/kb-repair design # Re-compile just one
/kb-repair -w myproject # Repair a specific workspace
Re-adding the same file also triggers automatic recovery.
Removal recovery: If a /kb-remove session is interrupted, Phase 1 completes
synchronously so the KB is always internally consistent. If Phase 2 (LLM cleanup)
is interrupted, affected concepts keep a needs_review: true flag that can be
cleared by re-running the removal or manually updating the concept.
Confirmation bypass: /kb-remove, /kb-clear, and /kb-ws-rm each show a
confirmation dialog before destructive operations. Pass -y (--yes) to skip
this dialog: /kb-remove -y my-doc, /kb-clear -y myproject. Useful in
headless/automated contexts.
Workspaces
Create isolated knowledge bases for different projects:
/kb-init myproject # Create workspace
/kb-add -w myproject design.md # Populate it
/kb-query -w myproject "what's the auth flow?"
Workspaces are stored as subdirectories under ~/.pi/agent/kb/workspaces/.
To delete a workspace and all its data:
/kb-ws-rm myproject # Deletes everything in workspace "myproject"
/kb-ws-rm default # Clears the default workspace (keeps named workspaces)
A confirmation dialog is shown before anything is removed (bypass with -y).
Deleting the default workspace (/kb-ws-rm default) clears its sources,
summaries, concepts, and index but preserves any named workspaces under
workspaces/.
How it works
Adding a document (/kb-add)
- The source file is copied into
source/and registered inregistry.jsonwithcompiled: false - The LLM receives a compile prompt with the document content
- Summary: The LLM writes a 200–400 word summary via
kb_write_summary. A deterministic footer is appended — concept links are extracted from the body and listed as[[concept/...]]references - Concepts: The LLM creates new topics via
kb_write_conceptor extends existing ones viakb_update_concept:kb_write_concept— creates a new concept with an explicit sources listkb_update_concept— updates an existing concept with new information. The new source is automatically merged with existing sources on the server. Old sources are preserved deterministically — the LLM never touches them
- Index: The LLM calls
kb_update_indexwhich filters entries against disk before writing — any slug the LLM invented that doesn't correspond to a real file is silently dropped - After the index is written, a footer sync pass regenerates every summary's
**Concepts**footer from the actual concept source lists on disk — guaranteeing footers are always in sync - The registry entry is marked
compiled: trueat the final step (the atomic commit point)
Adding inline content (/kb-add-content)
Paste text directly into the knowledge base without a file or URL:
/kb-add-content # My Notes on Rust
Rust is a systems programming language that...
- The text is hashed and saved as
source/inline-{hash}.mdwith a temporaryinternal-*docName - The LLM receives a compile prompt that instructs it to first choose a meaningful docName via
kb_set_docname(oldDocName, newDocName) - After renaming, the LLM follows the same compile pipeline as
/kb-add(summary → concepts → index) - If the LLM forgets to rename,
kb_write_summaryrejects temporaryinline-*names and prompts it to try again
Same deduplication, pending-compilation guard, and -f override apply.
Removing a document (/kb-remove)
Removal uses a two-phase staged pipeline. Phase 1 is entirely deterministic (no LLM); Phase 2 is an optional LLM cleanup.
Phase 1 — deterministic structural cleanup:
- Delete the summary file
- For each concept that references the removed document:
- If the document was the only source → delete the concept entirely
- If other sources remain → update the sources list, set
needs_review: truein frontmatter, keep the body intact (no data loss)
- Rebuild
index.mdfrom disk (scanning summaries/ and concepts/ directories) - Delete the source file from
source/ - Delete the registry entry last — at this point all wiki files are already consistent
Phase 2 — LLM surgical cleanup (non-critical):
- Only runs if concepts were affected
- The LLM reads each concept flagged
needs_review: true, surgically removes content traceable to the deleted document, and writes back withneeds_review: false - If the session is interrupted during Phase 2, the KB remains valid — concepts just have
needs_review: trueflags that can be resolved later with a re-run
File format
Summary (wiki/summaries/{docName}.md):
---
name: "architecture"
source: "architecture.md"
date_added: "2026-05-26T..."
---
<summary prose>
---
**Concepts**
[[concept/caching-strategy]]
Concept (wiki/concepts/{slug}.md):
---
name: "caching-strategy"
sources: [summary/architecture, summary/design]
date_added: "2026-05-26T..."
needs_review: false
---
<concept prose>
---
**Sources**
[[summary/architecture]]
[[summary/design]]
- Frontmatter — machine-readable metadata (
name,sources,date_added,needs_review) - Body — LLM-written prose
- Footer — deterministic (code-generated, not LLM): concepts list their sources, summaries list referencing concepts (synced post-compile from ground truth). All
[[...]]wiki links are generated by footers — the LLM writes plain markdown bodies
Recovery & repair
Compilation interrupted: If a /kb-add session is interrupted mid-compilation, the registry keeps compiled: false. /kb-status shows a ⚠ Pending compilation line. Run /kb-repair to resume.
Removal interrupted: If a /kb-remove session is interrupted after Phase 1 (which completes synchronously), the KB is already consistent. If interrupted during Phase 2, concepts retain needs_review: true flags. Re-running /kb-remove for the same document or manually calling kb_write_concept on the affected concepts clears the flag.
Failure modes — before vs after
| Failure | Before (LLM-driven) | After (deterministic) |
|---|---|---|
| LLM invents slug in index | Phantom entry | Filtered before write |
| Session interrupted mid-remove | Orphaned wiki files, no repair path | Phase 1 already committed; body preserved with needs_review flag |
| Concept body corrupted by remove | Undetectable | Sources list correct, body kept, flag for review |
| Registry deleted before cleanup | Inconsistent state | Registry deleted last, after all file ops |
| LLM forgets old sources when updating concept | Sources lost | Union from disk — old sources always preserved |
~/.pi/agent/kb/
├── registry.json # Hash-based dedup tracking
├── source/ # Original file copies
├── wiki/
│ ├── index.md # KB overview with one-liner entries
│ ├── summaries/ # Per-document summaries
│ └── concepts/ # Cross-document topic synthesis
└── workspaces/ # Named, isolated workspaces
└── myproject/
├── registry.json
├── source/
└── wiki/
├── index.md
├── summaries/
└── concepts/
Web Page Compatibility
/kb-add fetches pages using a plain HTTP request and converts the raw HTML to Markdown. This works well for static or server-rendered pages but will produce thin or empty results for JavaScript-heavy sites, since no browser or JS engine is involved.
Works well:
- Documentation sites (plain HTML, SSG output)
- Wikipedia, blog posts, news articles
- GitHub READMEs and rendered markdown pages
- Most technical references and man pages
Likely to fail or produce poor output:
- Single-page applications (React, Vue, Angular)
- Pages that require login or session cookies
- Sites behind Cloudflare or bot-detection challenges
- Content loaded via infinite scroll or lazy fetch
If a page produces an empty or garbled result, try finding a static mirror, an archived version at web.archive.org, or export the content manually as a .md file and use /kb-add <file.md> instead.
Query from anywhere
The knowledge base lives in ~/.pi/agent/kb/ — a fixed location in your home directory, not inside any project or repository. Once you've compiled documents, you can run /kb-query (with an optional -w workspace flag) from any directory on your machine. There's no need to be inside the repo where the source files originally came from.
Cross-reference documents across workspaces by switching between them:
/kb-query "how does this repo handle errors?"
/kb-query -w backend "how does this repo handle errors?"
Version controlling your KB
The ~/.pi/agent/kb/ folder is plain files — registry.json and markdown — so it's easy to track with Git if you want history, backups, or to sync across machines.
cd ~/.pi/agent/kb
git init
echo "source/" >> .gitignore # optionally skip raw source copies
git add .
git commit -m "initial kb snapshot"
From there, commit whenever you add documents, push to a private remote to back up or share the compiled wiki, and pull on another machine to restore it. Since /kb-add deduplicates via registry.json, the state will be consistent as long as the registry and wiki are in sync.
Requirements
- pi coding agent
Headless execution (RPC mode)
pi-kb commands are designed for interactive use in pi's TUI — the
terminal interface where ctx.ui.confirm() pops up a dialog and
ctx.ui.notify() renders inline status messages. When pi runs in
headless RPC mode (as the Keb bridge does), these UI interactions
translate to the extension UI protocol over stdin/stdout and have
important limitations.
How headless mode differs
| Feature | Interactive (TUI) | Headless (RPC) |
|---|---|---|
ctx.ui.confirm() |
Dialog in terminal, user responds | Emits extension_ui_request, blocks waiting for stdin response |
ctx.ui.notify() |
Inline status in terminal | Emits extension_ui_request (fire-and-forget) |
| Interrupted compilation | User can /kb-repair from session |
Bridge detects compiled: false in registry, re-compiles |
| Session persistence | Full session saved, resumable | --no-session — ephemeral, no resume |
Confirmation dialogs will hang
Several commands call ctx.ui.confirm() to ask the user a question
before proceeding. In RPC/headless mode, pi emits an
extension_ui_request event on stdout and blocks indefinitely
waiting for an extension_ui_response on stdin. If the headless
consumer (e.g. Keb bridge) does not send a response, pi hangs forever.
Commands that use confirmation dialogs:
/kb-add— when a pending compilation exists and-fis not passed/kb-add-content— same guard, asks to discard pending before adding new/kb-remove— confirms before deleting (bypass with-y)/kb-clear— confirms before clearing workspace (bypass with-y)/kb-ws-rm— confirms before deleting workspace (bypass with-y)
Commands that are safe in headless mode (no confirmation dialogs):
/kb-init— creates workspace, synchronous fs write only/kb-repair— recompiles pending docs, no confirms/kb-list,/kb-status,/kb-workspaces— read-only, no confirms/kb-query— sends LLM prompt, no confirms
Required contract for headless consumers
To avoid hangs, headless consumers must:
-
Always pass
-f(force) on/kb-addand/kb-add-content. This skips the "discard pending?" confirmation. The Keb bridge does this insrc/handlers/command-handler.jsandsrc/handlers/add-content-handler.js. -
Pass
-y(yes) to bypass confirmations on/kb-remove,/kb-clear, and/kb-ws-rm. Without-y, these commands block waiting for a confirm response on stdin. With-y, they proceed immediately (same as-ffor/kb-add). Keb bridge does not use these commands, but if a consumer needs them, always pass-y. -
Treat
extension_ui_requestevents as optional. The fire-and-forget ones (notify,setStatus, etc.) can be silently ignored. Dialog ones (confirm,select, etc.) should never be received if the consumer follows rules 1 and 2.
Notification loss
ctx.ui.notify() calls in headless mode emit extension_ui_request
with method: "notify". The Keb bridge forwards these as generic
WebSocket events, but the Chrome extension client does not render them.
This means informational messages like "Fetching:
This is an intentional design choice: the Chrome extension aims for
simplicity and does not replicate pi's full TUI. Consumers who want
rich status reporting can listen for extension_ui_request events
and render them themselves.
Design rationale
pi-kb is an interactive-first extension. Confirmation dialogs and
notifications are essential to its normal UX. Rather than maintaining
two code paths (headless vs interactive), pi-kb keeps one code path
and documents the contract for headless consumers. The Keb bridge
honors this contract by always passing -f and avoiding
dialog-triggering commands.