workspace-kb
by phuhao00
README.md
# workspace-kb
> English · [中文文档](./README.zh-CN.md)
Local [LanceDB](https://github.com/lancedb/lancedb) knowledge base for **agent-friendly** markdown workspaces.
Index curated docs / skills / wiki — **not** your full source tree. Agents use **MCP** (`kb_search` → `kb_read`); scripts use the **CLI**. Search returns short snippets; reads open one heading.
| | |
|---|---|
| Version | **1.5** |
| License | MIT |
| Repo | https://github.com/phuhao00/workspace-kb |
## Why
- Cut token waste: agents get **snippets + one section**, not whole manuals.
- Route multi-repo workspaces: find the right doc/repo first, then `rg` locally.
- **Project memory**: share ops/triage facts across Cursor · Codex · CLI — tags, redaction, TTL, dashboard audit.
- Dashboard tracks proxy savings (`chars/4`) and MCP health — not LLM billing.
## Requirements
- Node.js **>= 20**
- One embedding backend:
- **Ollama** (default): `ollama pull bge-m3`
- **OpenAI / compatible**: `"embedProvider": "openai"` + `OPENAI_API_KEY`
```bash
ollama pull bge-m3
# low VRAM alternative:
# WORKSPACE_KB_MODEL=nomic-embed-text # set matching embedDim in config
```
## Quick start
```bash
npx workspace-kb init
npm install
npx workspace-kb ingest
# one command: sync MCP/port config + start dashboard (auto incremental ingest on)
npx workspace-kb up --port 8700
# open http://127.0.0.1:8700/
npx workspace-kb search "payment failure"
npx workspace-kb down
```
New/changed markdown under configured paths is **auto-ingested** while the dashboard runs (~2s debounce). Also:
```bash
npx workspace-kb watch # auto-ingest only
npx workspace-kb up --no-watch # start without watching
```
Config: `"autoIngest": false` to disable; `"watchDebounceMs": 3000` to tune.
Existing project:
```bash
npm install github:phuhao00/workspace-kb#master # pin #master — old builds lack up/start
npx workspace-kb up --port 8700
# first time / big doc changes: npx workspace-kb up --port 8700 --ingest
```
## Install / upgrade
```bash
npm install github:phuhao00/workspace-kb#master
```
### Auto-setup (install · update · ingest · `setup`)
If `workspace-kb.config.json` is found (cwd or parent, or `INIT_CWD` on postinstall), the package writes:
| Path | Purpose |
|------|---------|
| `.cursor/mcp.json` | MCP server (default **HTTP** → `http://127.0.0.1:<port>/mcp`) |
| `.cursor/rules/workspace-kb-routing.mdc` | `alwaysApply` — call `kb_search` first |
| `.cursor/skills/query-workspace-kb/SKILL.md` | Cursor skill for architecture/ops questions |
| `.continue/workspace-kb.mcp.json` | Continue.dev merge snippet |
| `AGENTS.md` `<!-- WORKSPACE-KB:… -->` | Agent routing (skip if `agentsMd: false` or custom `kb_search` docs) |
Disable: `WORKSPACE_KB_SKIP_SETUP=1` or `"setup": { "enabled": false }`.
After upgrade: **restart `start`/`serve`**, then click **重启 MCP** on the dashboard (or reload Cursor MCP).
## CLI reference
```text
workspace-kb <command>
up [--port <n|auto>] [--ingest|--full] [--no-watch] # ★ sync+start+auto-ingest
down [--port <n>] # ★ stop
watch # auto-ingest only (foreground)
init [--force] [--name app] [--port <n>]
setup [--port <n>]
ingest [--full]
start | stop | serve [--port <1-65535|auto>] [--no-watch]
search | read | status | health | stats | projects | memory | feedback
```
Day-to-day: `up` / `down`. While the dashboard runs, markdown under configured paths is watched and incrementally ingested.
## Dashboard
```bash
npx workspace-kb start # detached (pid under ~/.workspace-kb/)
# or foreground:
npx workspace-kb serve --port 8787
```
Open **http://127.0.0.1:8787/**
| Panel | What you can do |
|-------|-----------------|
| **控制** | Restart MCP · re-setup · incremental/full ingest · restart server |
| **健康检查** | config · workspaceRoot · index · embed · MCP · hit rate |
| **项目记忆** | Put / delete by key·id / prune expired; audit list |
| **多项目** | Local registered instances + ports |
| **Metrics** | calls · hit rate · est. returned/saved tokens |
| **Recent events** | 👍/👎 feedback → path boost in search |
**HTTP MCP:** `http://127.0.0.1:<port>/mcp` — Cursor/Continue need `start`/`serve` running.
### Local HTTP APIs
| Method | Path | Notes |
|--------|------|-------|
| GET | `/api/health` | Health checks + hit-rate summary |
| GET | `/api/control` | Status, projects, feedback, ingest job, memory preview |
| GET | `/api/memory` | List project memory facts |
| GET | `/api/usage?days=7` | Token proxy dashboard payload |
| GET | `/api/projects` | Multi-project registry |
| GET | `/api/feedback` | Feedback summary |
| POST | `/api/actions/restart-mcp` | Drop MCP sessions (clients reconnect) |
| POST | `/api/actions/setup` | Re-run auto-setup |
| POST | `/api/actions/ingest` | Body `{ "full": true }` optional |
| POST | `/api/actions/feedback` | `{ useful, query, path, heading }` |
| POST | `/api/actions/memory-put` | `{ text, key?, tags?, ttlDays?, source? }` |
| POST | `/api/actions/memory-delete` | `{ id? }` or `{ key? }` |
| POST | `/api/actions/memory-prune` | Remove expired rows from disk |
| POST | `/api/actions/restart-server` | Respawn dashboard |
| * | `/mcp` | Streamable HTTP MCP |
## Project memory (v1.5)
Shared **project ops facts** across Cursor + Codex + CLI (env topology, staging ports, redacted triage conclusions).
**Not** IDE personal Memories, and **not** a substitute for `AGENTS.md` or the doc index.
### What belongs where
| Store | Use for | Example |
|-------|---------|---------|
| Cursor / Codex Memories | Personal preferences | “I prefer TypeScript” |
| `AGENTS.md` / `.cursor/rules` | Team hard rules (git-committed) | Branch model, no prod config edits |
| `kb_search` / `kb_read` | Docs · skills · wiki | One section of a payment runbook |
| `kb_memory_*` | Shared ops facts (redacted, TTL, dashboard audit) | “Staging hallapi HTTP :8080” |
Good writes: staging hosts/ports, **redacted** incident conclusions, temporary feature flags.
Never write: passwords / API keys / tokens, ID numbers / PII, pure personal preferences.
### CLI
```bash
# upsert by --key; default TTL 90 days; --ttl 0 = never expire
npx workspace-kb memory put "staging hallapi HTTP :8080" \
--key staging-hallapi --tags ops,test-env --ttl 90
npx workspace-kb memory search "hallapi"
npx workspace-kb memory list
npx workspace-kb memory delete --key staging-hallapi
# or: npx workspace-kb memory delete --id <uuid>
npx workspace-kb memory prune # drop expired rows
```
| Subcommand | Notes |
|------------|-------|
| `put` | `--key` stable upsert key (else derived from text); `--tags a,b`; `--ttl days` |
| `search` | Score against key / text / tags |
| `list` | Recent non-expired (default limit 50) |
| `delete` | `--key` or `--id` |
| `prune` | Remove expired lines from `facts.jsonl` |
### MCP tools
| Tool | Role |
|------|------|
| `kb_memory_put` | Write; required `text`; optional `key` / `tags` / `ttlDays` / `source` |
| `kb_memory_search` | Search; optional `query` / `tag` / `limit` |
| `kb_memory_list` | List; optional `limit` / `includeExpired` |
| `kb_memory_delete` | Delete by `id` or `key` |
`kb_search` also returns `relatedMemories` (up to ~3 matching ops facts).
`kb_status` includes a short recent-memory peek.
### Validation and TTL
Same checks for dashboard / CLI / MCP:
- Non-empty text, max **2000** characters
- Looks like a preference (“I prefer…”, “我喜欢…”) → **rejected** — use IDE Memories
- Looks like secrets / Bearer / private key / ID card → **rejected** — redact first
- **TTL**: default **90** days; `0` = never; max 3650 days
- Same `key` **upserts** (replaces the previous row)
### Data shape
Path: `.workspace-kb/memory/facts.jsonl` (one JSON object per line; git-ignore `dataDir`).
```json
{
"id": "uuid",
"ts": "2026-09-02T09:00:00.000Z",
"key": "staging-hallapi",
"text": "staging hallapi HTTP :8080",
"tags": ["ops", "test-env"],
"source": "cli",
"ttlDays": 90,
"expiresAt": "2026-12-01T09:00:00.000Z"
}
```
`expiresAt: null` means never. Expired rows are hidden from search/list by default; use `prune` or the dashboard button to delete them from disk.
### Agent workflow
1. Before re-investigating: `kb_memory_search` (or check `relatedMemories` on `kb_search`).
2. After a durable, redacted conclusion: `kb_memory_put` with tags (`ops`, service, env).
3. Drop stale facts on the **项目记忆** panel, or use a short TTL then `prune`.
## Features (v1.4+)
| Area | Behavior |
|------|----------|
| Hybrid search | Vector + lexical (incl. CJK bigrams) + kind boost + feedback boost |
| Query rewrite | Builtin CN/EN synonyms; extend via `synonyms` in config |
| Incremental ingest | File fingerprints + vector cache; unchanged files skip embed; `--full` rebuilds |
| **Project memory** | `.workspace-kb/memory/`; MCP + CLI + dashboard; TTL / redact / tags |
| Daemon | `start` / `stop` / `projects`; registry at `~/.workspace-kb/registry.json` |
| Cloud embed | `embedProvider: "openai"` (+ `openaiBaseUrl` for compatible APIs) |
| Continue | Auto snippet + [`examples/continue.mcp.json`](examples/continue.mcp.json) |
## Configure
Put `workspace-kb.config.json` at the **workspace root** (or set `WORKSPACE_KB_CONFIG`):
```json
{
"workspaceRoot": ".",
"dataDir": ".workspace-kb",
"model": "bge-m3",
"embedDim": 1024,
"embedProvider": "ollama",
"incremental": true,
"rewriteQuery": true,
"hybridVectorWeight": 0.65,
"hybridLexicalWeight": 0.35,
"synonyms": {
"登不进": ["登录", "login", "token"],
"充值未到账": ["支付", "pay", "订单", "callback"]
},
"paths": [".agents", "docs", "openwiki", "*.md", ".cursor/skills"],
"childRepos": ["api", "web"],
"childGlobs": ["README*.md", "docs/**/*.md"],
"skipDirs": ["node_modules", "vendor", ".git", ".next", "Library", "logs", ".workspace-kb"],
"setup": {
"mcpServerId": "my-project-kb",
"dashboardPort": 19090,
"mcpMode": "http",
"agentsMd": true,
"cursorSkill": true,
"continueConfig": true
}
}
```
### OpenAI / compatible embeddings
```json
{
"embedProvider": "openai",
"model": "text-embedding-3-small",
"embedDim": 1536,
"openaiBaseUrl": "https://api.openai.com/v1"
}
```
```bash
set OPENAI_API_KEY=sk-...
npx workspace-kb ingest --full
```
### Data layout (git-ignore `dataDir`)
```text
.workspace-kb/
lancedb/ # LanceDB table
meta.json
usage.jsonl # search/read proxy metrics
feedback.jsonl # 👍/👎
fingerprints.json # incremental ingest
cache/vectors.json # embedding cache
memory/
facts.jsonl # project ops memory (shared across tools)
```
### MCP modes
| `setup.mcpMode` | `.cursor/mcp.json` | Notes |
|-----------------|--------------------|-------|
| `http` (default) | `{ "url": "http://127.0.0.1:<dashboardPort>/mcp" }` | Needs `start`/`serve`; dashboard can **重启 MCP** |
| `stdio` | `command` + `env.WORKSPACE_KB_CONFIG` | No dashboard required; pin config path (Cursor often ignores `cwd`) |
## Multiple projects on one machine
Each workspace has its **own** config + `.workspace-kb/`. Use **any free TCP port** (`1–65535`). `8787` / `8788` are examples only — not a hard limit.
Resolution order: `--port` → `WORKSPACE_KB_PORT` → `setup.dashboardPort` → local registry → default `8787`.
```bash
# pin any port in config:
# "setup": { "dashboardPort": 19090 }
npx workspace-kb start # config port (also syncs MCP URL)
npx workspace-kb start --port 19091 # override + rewrite config + `.cursor/mcp.json`
npx workspace-kb start --port auto # OS free port + sync bindings
npx workspace-kb stop # stops config/registry port
npx workspace-kb setup --port 19090 # rewrite MCP URL + persist config only
npx workspace-kb projects
```
`start` / `serve` sync after the listen port is chosen: `setup.dashboardPort`, `.cursor/mcp.json`, and `.continue/workspace-kb.mcp.json`.
Prefer **project-local** `.cursor/mcp.json`. Examples: [`examples/multi-a`](examples/multi-a), [`examples/multi-b`](examples/multi-b), [`examples/multi-project.mcp.json`](examples/multi-project.mcp.json).
### Indexing Chinese docs
- Keep `"*.md"` in `paths` so root `README.md` / `README.zh-CN.md` are ingested.
- Prefer clear Chinese `##` headings under `docs/` for `kb_read`.
- Add spoken symptoms to `synonyms` (e.g. 登不进, 充值未到账). Full guide: [README.zh-CN.md](./README.zh-CN.md).
## Cursor / agent tips
1. Keep `npx workspace-kb start` running (HTTP MCP).
2. Ask architecture/ops questions — rule + skill should trigger `kb_search`.
3. Before re-triage: `kb_memory_search`; after a durable conclusion: `kb_memory_put` (redact + tags + TTL).
4. Confirm usage: `Get-Content .workspace-kb/usage.jsonl` or dashboard Recent events.
5. If `kb_status` shows `workspaceRoot` under your home directory, MCP is mis-bound — use HTTP mode or pin `WORKSPACE_KB_CONFIG`.
## Troubleshooting
| Symptom | Fix |
|---------|-----|
| `EADDRINUSE :<port>` | Instance already on that port — use it, `stop --port <n>`, or change `setup.dashboardPort` |
| Dashboard old UI / no **项目记忆** / 404 `/api/health` | Kill old node on that port; reinstall `#master` (≥1.5) and `start` again |
| `No usage.jsonl` | Only **search/read/MCP** write usage — `ingest` alone does not |
| Empty search / low hit rate | `ingest`, check `health`, extend `synonyms`, verify MCP root |
| `memory put` rejected (preference / secret) | Preferences → IDE Memories; redact secrets then put |
| Memory file has lines but search is empty | Likely expired — check `expiredCount` on `memory list`, then `prune` |
| Ollama errors | `ollama serve` + `ollama pull <model>` |
| Agent never calls KB | Restart MCP; confirm rule/skill exist; ask explicitly “先 kb_search …” |
## Env
| Variable | Meaning |
|----------|---------|
| `WORKSPACE_KB_CONFIG` | Absolute path to config JSON |
| `WORKSPACE_ROOT` | Override workspace root |
| `WORKSPACE_KB_MODEL` / `BUYU_KB_MODEL` | Embedding model id |
| `WORKSPACE_KB_DIM` / `BUYU_KB_DIM` | Expected dimension |
| `WORKSPACE_KB_EMBED_PROVIDER` | `ollama` \| `openai` |
| `OPENAI_API_KEY` / `OPENAI_BASE_URL` | Cloud embeddings |
| `OLLAMA_HOST` | Default `http://127.0.0.1:11434` |
| `WORKSPACE_KB_PORT` | Dashboard port (any `1–65535`, or `auto`; else `setup.dashboardPort`) |
| `WORKSPACE_KB_SKIP_SETUP` | `1` skips postinstall setup |
## License
MIT
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues