feat: Add cross-KB search, inline health checks, and simplify templates
All checks were successful
Tests / test (push) Successful in 13m33s

- Add search_all_knowledge_bases MCP tool + POST /api/kb/search-all endpoint
  + query_documents_multi_kb() in sqlite_manager with dedup and score merge
- Add inline health check to search_knowledge_base (24h cache, appends warnings)
- Rewrite CLAUDE.md template: 351→128 lines, project content first, session tips
- Simplify session command: 625→224 lines, 4-field state file
- Update README.md What's New to v2.5, remove Discord section
- Add cross-KB search tool to CLAUDE.md MCP tools table
- Add 5 new tests for query_documents_multi_kb (all 593 tests pass)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Bob Roberts 2026-02-06 11:04:21 -06:00
parent ecd040d08a
commit ee7b62bc5b
8 changed files with 569 additions and 850 deletions

View file

@ -44,6 +44,7 @@ Claude OS provides MCP tools (prefixed with `mcp__code-forge__`) for managing kn
|------|---------|
| `mcp__code-forge__list_knowledge_bases` | List all your knowledge bases |
| `mcp__code-forge__search_knowledge_base` | Search a KB with RAG |
| `mcp__code-forge__search_all_knowledge_bases` | Search across multiple KBs at once |
| `mcp__code-forge__create_knowledge_base` | Create a new KB |
| `mcp__code-forge__get_kb_stats` | Get statistics for a KB |
| `mcp__code-forge__list_documents` | List documents in a KB |
@ -68,6 +69,13 @@ mcp__code-forge__search_knowledge_base
query: "authentication patterns"
```
**Search across all KBs at once:**
```
mcp__code-forge__search_all_knowledge_bases
query: "authentication patterns"
kb_filter: "MyProject-"
```
---
## Slash Commands

101
README.md
View file

@ -30,18 +30,6 @@
---
<p align="center">
<a href="https://discord.gg/BrvVYdXkCU">
<img src="https://img.shields.io/badge/Join_our_Discord!-5865F2?style=for-the-badge&logo=discord&logoColor=white" alt="Join Discord">
</a>
</p>
<p align="center">
<strong>We have a Discord!</strong> Join our community to get help, share your projects, and connect with other Claude OS users.
</p>
---
## ✨ The Magic
**You:** "Remember this: our API uses JWT tokens with 15-minute expiry and 7-day refresh tokens"
@ -60,80 +48,65 @@ That's it. Next week, next month, next project - Claude remembers. No commands t
---
## 🆕 What's New in v2.3
## 🆕 What's New in v2.5
> **Latest Release: December 2025**
> **Latest Release: February 2026**
### 🎯 Skills Library & Community Skills
### 🔍 Cross-KB Search
Browse and install **36+ skills** from the community with one click!
Search across **all your knowledge bases at once** with a single query!
```bash
/claude-os-skills # List all skills
/claude-os-skills install pdf # Install from community
```
mcp__code-forge__search_all_knowledge_bases
query: "authentication patterns"
kb_filter: "MyProject-" # optional: limit to one project's KBs
```
**New Features:**
- 📚 **Local Templates** - Pre-built skills for Rails, React, testing workflows
- 🌐 **Community Skills** - Install from Anthropic Official (16) & Superpowers (20)
- 🔧 **Custom Skills** - Create and share project-specific skills
- 🎨 **Beautiful UI** - Browse, search, and install via web interface
- 📖 **[Recommended Skills](https://github.com/brobertsaz/claude-os/wiki/Recommended-Skills)** - Our curated list of skills we actually use and trust
- Results merged by relevance score with KB attribution
- Automatic deduplication across KBs
- Optional prefix filter to scope by project
- New API endpoint: `POST /api/kb/search-all`
### 📊 Session Insights
### 🩺 Inline Health Checks
Parse Claude Code sessions and extract insights automatically!
Health checks now run **automatically** during search — no manual invocation needed.
**New Features:**
- 🔍 **Session Parser** - Reads Claude Code's native `.jsonl` session files
- 💡 **Insight Extraction** - Extracts patterns, decisions, blockers
- 📈 **Analytics Ready** - Track tool usage and file changes across sessions
- When you search a KB, Claude OS checks if a health report has been run in the last 24 hours
- If stale, runs a quick health check and appends HIGH/CRITICAL warnings to the search result
- Cached in `data/health_cache.json` — never slows down or breaks search
- Look for `_health_warnings` in search results
### ✨ Beautiful New Installer (v2.2)
### 📝 Simplified Session Management
The installer got a major upgrade with **Charm CLI (gum)** support!
Session state reduced from a 50-field JSON blob to **4 fields**:
```bash
./setup-claude-os.sh --demo # See the beautiful UI without changes
./setup-claude-os.sh --dry-run # Preview what would happen
./setup-claude-os.sh --help # See all options
```json
{
"last_task": "Fix appointment email flood",
"last_branch": "fix-email-flood",
"stopped_at": "2026-02-06T18:30:00Z",
"one_liner": "Fixed dedup check, still need rate limiting"
}
```
**New Features:**
- 🎨 **Gum Integration** - Beautiful interactive menus with arrow-key navigation (with graceful bash fallback)
- 🛡️ **Safety Features** - `--demo`, `--dry-run`, and automatic config backups
- 💨 **Lite Model Default** - Now defaults to `llama3.2:3b` (2GB) instead of 8b (4.7GB)
- ☁️ **Cloud Option** - Choose between Local (Ollama) or Cloud (OpenAI) during setup
- 🐧 **Better Linux Support** - Improved package manager detection and installation
- START: read state, show one-liner, search memories, ready
- END: git diff summary, offer to save, write state
- `save`, `blocker`, `pattern` sub-commands unchanged
**Safety First:**
- `--demo` - Try the installer UI without making any changes
- `--dry-run` - See exactly what would be done before doing it
- Auto-backup - Existing `.env` files backed up before overwriting
### 📄 Leaner CLAUDE.md Template
### 🔬 Knowledge Lifecycle Engine
Project CLAUDE.md template cut from 351 to **128 lines**:
Keep your knowledge bases healthy and focused!
```bash
/claude-os-lifecycle health my-kb # Health report with recommendations
/claude-os-lifecycle dedup my-kb # Find and merge duplicate memories
/claude-os-lifecycle consolidate my-kb # LLM-powered document merging
/claude-os-lifecycle archive my-kb # Find stale docs, archive/restore
```
**New Features:**
- 🔍 **Duplicate Detection** - Embedding-based similarity scanning with union-find clustering
- 🧹 **Smart Merging** - Keep the best doc, delete the rest, or LLM-merge into one
- 📦 **Archival System** - Soft-archive stale docs without permanent deletion
- 📊 **Health Reports** - Embedding coverage, age distribution, actionable recommendations
- 📈 **Growth Timeline** - Track KB growth over time by day/week/month
- 📋 **Audit Logging** - Full operation history for all lifecycle actions
- Removed 200-line mandatory session protocol (6-phase startup, ASCII prompts)
- Replaced with 4-line "Session Tips" section
- Project content first, Claude OS section second
- All template variables preserved
### 📋 Recent Improvements
| Version | Highlights |
|---------|------------|
| **v2.5** | Cross-KB search, inline health checks, simplified sessions, leaner templates |
| **v2.4** | Knowledge lifecycle engine (dedup, consolidate, archive, health) |
| **v2.3** | Skills library, community skills, session insights |
| **v2.2** | Gum CLI support, safety features, lite model default |

View file

@ -424,6 +424,76 @@ class SQLiteManager:
finally:
conn.close()
def query_documents_multi_kb(
self,
kb_names: List[str],
query_embedding: List[float],
n_results: int = 10
) -> Dict[str, Any]:
"""Query documents across multiple knowledge bases, merge by score, deduplicate."""
conn = self.get_connection()
try:
cursor = conn.cursor()
query_vec = np.array(query_embedding, dtype=np.float32)
all_results: List[Tuple[str, str, str, float, str]] = [] # (doc_id, content, metadata, similarity, kb_name)
for kb_name in kb_names:
cursor.execute("SELECT id FROM knowledge_bases WHERE name = ?", (kb_name,))
row = cursor.fetchone()
if not row:
continue
kb_id = row['id']
cursor.execute(
"SELECT doc_id, content, embedding, metadata FROM documents WHERE kb_id = ?",
(kb_id,)
)
for doc_row in cursor.fetchall():
if doc_row['embedding'] is None:
continue
emb = np.frombuffer(doc_row['embedding'], dtype=np.float32)
dot_product = np.dot(query_vec, emb)
norm_query = np.linalg.norm(query_vec)
norm_emb = np.linalg.norm(emb)
if norm_query > 0 and norm_emb > 0:
similarity = float(dot_product / (norm_query * norm_emb))
else:
similarity = 0.0
all_results.append((doc_row['doc_id'], doc_row['content'], doc_row['metadata'], similarity, kb_name))
# Sort by similarity descending
all_results.sort(key=lambda x: x[3], reverse=True)
# Deduplicate by content prefix (first 200 chars)
seen_prefixes: set = set()
deduped: List[Tuple[str, str, str, float, str]] = []
for r in all_results:
prefix = (r[1] or "")[:200]
if prefix in seen_prefixes:
continue
seen_prefixes.add(prefix)
deduped.append(r)
top = deduped[:n_results]
ids = [r[0] for r in top]
documents = [r[1] for r in top]
metadatas = []
for r in top:
meta = json.loads(r[2]) if r[2] else {}
meta["_source_kb"] = r[4]
metadatas.append(meta)
scores = [r[3] for r in top]
return {
"ids": ids,
"documents": documents,
"metadatas": metadatas,
"scores": scores
}
finally:
conn.close()
def collection_exists(self, kb_name: str) -> bool:
"""Check if a knowledge base exists."""
conn = self.get_connection()

View file

@ -27,6 +27,8 @@ import asyncio
import json
import os
import sys
import time
from pathlib import Path
from typing import Any
import httpx
@ -37,6 +39,65 @@ from mcp.types import Tool, TextContent
# Configuration
API_BASE = os.environ.get("CLAUDE_OS_API", "http://localhost:8051")
# Health cache — tracks last health check time per KB and any warnings
HEALTH_CACHE: dict[str, dict] = {}
HEALTH_CACHE_FILE = os.path.join(
os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "data", "health_cache.json"
)
HEALTH_CHECK_INTERVAL = 86400 # 24 hours
def _load_health_cache() -> None:
"""Load health cache from disk."""
global HEALTH_CACHE
try:
if os.path.exists(HEALTH_CACHE_FILE):
with open(HEALTH_CACHE_FILE, "r") as f:
HEALTH_CACHE = json.load(f)
except Exception:
HEALTH_CACHE = {}
def _save_health_cache() -> None:
"""Persist health cache to disk."""
try:
Path(HEALTH_CACHE_FILE).parent.mkdir(parents=True, exist_ok=True)
with open(HEALTH_CACHE_FILE, "w") as f:
json.dump(HEALTH_CACHE, f)
except Exception:
pass
async def _maybe_check_health(kb_name: str) -> list[str]:
"""Run a health check if stale (>24h) for this KB. Returns warning strings."""
_load_health_cache()
now = time.time()
entry = HEALTH_CACHE.get(kb_name)
if entry and (now - entry.get("checked_at", 0)) < HEALTH_CHECK_INTERVAL:
return entry.get("warnings", [])
# Run health check
try:
async with httpx.AsyncClient() as client:
resp = await client.get(api_url(f"/api/kb/{kb_name}/lifecycle/health"), timeout=15.0)
resp.raise_for_status()
health = resp.json()
warnings = []
recs = health.get("recommendations", [])
for rec in recs:
severity = rec.get("severity", "")
msg = rec.get("message", rec.get("recommendation", ""))
if severity in ("high", "critical") and msg:
warnings.append(f"[{severity.upper()}] {msg}")
HEALTH_CACHE[kb_name] = {"checked_at": now, "warnings": warnings}
_save_health_cache()
return warnings
except Exception:
# Never break search — just skip the health check
return []
# Initialize MCP server - named "code-forge" for backwards compatibility
server = Server("code-forge")
@ -457,6 +518,21 @@ async def list_tools() -> list[Tool]:
},
"required": ["kb_name", "action"]
}
),
# Cross-KB Search
Tool(
name="search_all_knowledge_bases",
description="Search across multiple knowledge bases at once. Returns merged results sorted by relevance with KB attribution.",
inputSchema={
"type": "object",
"properties": {
"query": {"type": "string", "description": "Search query"},
"top_k": {"type": "integer", "default": 10, "description": "Number of results to return"},
"kb_filter": {"type": "string", "description": "Optional prefix filter for KB names (e.g. 'Pistn-' to search only Pistn KBs)"}
},
"required": ["query"]
}
)
]
@ -493,11 +569,16 @@ async def _execute_tool(name: str, args: dict[str, Any]) -> dict:
})
elif name == "search_knowledge_base":
return await api_post(f"/api/kb/{args['kb_name']}/chat", {
result = await api_post(f"/api/kb/{args['kb_name']}/chat", {
"query": args["query"],
"top_k": args.get("top_k", 5),
"use_hybrid": args.get("use_hybrid", True)
})
# Inline health check — append warnings if stale
warnings = await _maybe_check_health(args["kb_name"])
if warnings:
result["_health_warnings"] = warnings
return result
elif name == "get_kb_stats":
return await api_get(f"/api/kb/{args['kb_name']}/stats")
@ -672,6 +753,14 @@ async def _execute_tool(name: str, args: dict[str, Any]) -> dict:
else:
return {"error": f"Unknown archive action: {action}"}
# Cross-KB Search
elif name == "search_all_knowledge_bases":
return await api_post("/api/kb/search-all", {
"query": args["query"],
"top_k": args.get("top_k", 10),
"kb_filter": args.get("kb_filter")
})
else:
return {"error": f"Unknown tool: {name}"}

View file

@ -556,6 +556,11 @@ class ChatRequest(BaseModel):
use_rerank: bool = False
use_agentic: bool = False
class SearchAllRequest(BaseModel):
query: str
top_k: int = 10
kb_filter: Optional[str] = None
# REST API Endpoints for UI
@app.get("/api/kb")
@ -578,6 +583,56 @@ async def api_create_knowledge_base(request: CreateKBRequest):
return result
@app.post("/api/kb/search-all")
@limiter.limit("10/minute")
async def api_search_all(request: Request, body: SearchAllRequest):
"""REST API: Search across multiple knowledge bases."""
try:
from llama_index.embeddings.ollama import OllamaEmbedding
from app.core.config import Config
db_manager = get_sqlite_manager()
all_kbs = db_manager.list_collections()
kb_names = [kb["name"] for kb in all_kbs]
if body.kb_filter:
kb_names = [n for n in kb_names if n.startswith(body.kb_filter)]
if not kb_names:
return {"results": [], "kbs_searched": 0, "total_kbs": len(all_kbs)}
embed_model = OllamaEmbedding(
model_name=Config.OLLAMA_EMBED_MODEL,
base_url="http://localhost:11434"
)
query_embedding = embed_model.get_text_embedding(body.query)
raw = db_manager.query_documents_multi_kb(
kb_names=kb_names,
query_embedding=query_embedding,
n_results=body.top_k
)
results = []
for i in range(len(raw["ids"])):
results.append({
"doc_id": raw["ids"][i],
"text": raw["documents"][i],
"score": raw["scores"][i],
"kb_name": raw["metadatas"][i].get("_source_kb", ""),
"metadata": raw["metadatas"][i]
})
return {
"results": results,
"kbs_searched": len(kb_names),
"total_kbs": len(all_kbs)
}
except Exception as e:
logger.error(f"Search-all failed: {e}")
raise HTTPException(status_code=500, detail=str(e))
@app.delete("/api/kb/{kb_name}")
async def api_delete_knowledge_base(kb_name: str):
"""REST API: Delete a knowledge base."""

View file

@ -1,18 +1,16 @@
---
name: claude-os-session
description: Intelligent session management with automatic context loading and pattern recognition
description: Lightweight session management with automatic context loading
---
# Claude OS Session Management
Power-user session workflows that make me smarter with every session.
Simple session tracking that loads context on start and saves learnings on end.
## Setup
The state file is **project-local**: `{cwd}/claude-os-state.json` (where `{cwd}` is the current working directory).
Each project has its own session state, so you can have different active sessions in different projects.
## Commands
```
@ -22,309 +20,111 @@ Each project has its own session state, so you can have different active session
/claude-os-session save [note] - Quick save during session
/claude-os-session blocker [desc] - Track blocker
/claude-os-session pattern [desc] - Document pattern discovered
/claude-os-session context - Show loaded context
```
---
## START SESSION - The Smart Way
### What Happens
## START SESSION
```
/claude-os-session start "redesign appointment dashboard"
```
**Phase 1: Load State**
```
Read: {cwd}/claude-os-state.json
```
If the file doesn't exist, create it with the default template (see below).
**Step 1: Read State**
Read `{cwd}/claude-os-state.json`. If it exists and has content, show:
```
Last time: [one_liner]
Branch: [last_branch]
Stopped: [stopped_at]
```
If the file doesn't exist, that's fine — skip to Step 2.
**Step 2: Search Memories**
**Phase 2: Search Recent Memories**
```
[AUTOMATIC MEMORY SEARCH]
mcp__code-forge__search_knowledge_base
Query: "dashboard redesign security recent work"
KB: {project}-project_memories
kb_name: {project}-project_memories
query: "[task] recent"
```
**Phase 3: Get Git Context**
Show the top 3-5 results as key context.
**Step 3: Git Context**
```bash
git branch --show-current
git status --short
git log -5 --oneline
git log -3 --oneline
```
**Phase 4: Intelligent Context Loading**
**Step 4: Ready**
Based on task + recent memories + git state, I'll:
1. Find the 5 most relevant memories
2. Show you what I remember about this task
3. Identify patterns we've used before
4. Check for any blockers from last session
5. Load coding standards from project profile
**Phase 5: Show Session Start Summary**
Show a brief summary and start working:
```
═══════════════════════════════════════
🚀 SESSION STARTED
═══════════════════════════════════════
Session: [task]
Branch: [current-branch]
Context: [N] memories loaded
[One-line summary of each relevant memory]
Task: Redesign Appointment Dashboard
Branch: feature/appointment-redesign
Started: 2025-10-29 10:30 AM
📚 CONTEXT LOADED (5 memories):
✓ Appointment Dashboard Redesign Plan (Oct 28)
✓ Current Dashboard Analysis (Oct 28)
✓ Live Dashboard Analyzed (Oct 28)
✓ Bootstrap to Modern Cards Pattern (Oct 25)
✓ Sidebar Navigation Pattern (Oct 22)
🎯 KEY INSIGHTS:
• 67-page implementation plan ready
• All 5 tabs documented with screenshots
• Zero functionality loss requirement
• iOS-style toggles already in use
• Tekmetric integration must be preserved
🔄 PATTERNS AVAILABLE:
• Sidebar navigation (from user-auth work)
• Card-based layouts (from reports redesign)
• Modern toggle switches (existing in app)
⚠️ BLOCKERS FROM LAST SESSION:
None found ✓
📋 CODING STANDARDS:
• Service objects for business logic
• Decorator pattern with Draper
• Fragment caching with Redis
• Bootstrap 3.x styling
Ready to code! I have full context.
═══════════════════════════════════════
Ready to go.
```
**Phase 6: Update State**
**Step 5: Update State**
Write to `{cwd}/claude-os-state.json`:
```json
{
"current_session": {
"active": true,
"started_at": "2025-10-29T10:30:00Z",
"task": "redesign appointment dashboard",
"branch": "feature/appointment-redesign",
"context": [
"appointment_dashboard_redesign.md",
"current_dashboard_analysis.md",
...
]
}
"last_task": "[task]",
"last_branch": "[current-branch]",
"stopped_at": "[current ISO timestamp]",
"one_liner": ""
}
```
---
## END SESSION - The Smart Way
### What Happens
## END SESSION
```
/claude-os-session end
```
**Phase 1: Session Analysis**
**Step 1: Summarize Work**
I'll analyze what we accomplished:
- Files changed
- Commits made
- Patterns used
- Problems solved
- Decisions made
**Phase 2: Smart Save Prompts**
```
═══════════════════════════════════════
📊 SESSION SUMMARY
═══════════════════════════════════════
Duration: 2 hours 15 minutes
Task: Redesign Appointment Dashboard
Files Changed: 8 files
Commits: 3 commits
🎨 WORK COMPLETED:
✓ Created sidebar navigation component
✓ Converted Bootstrap panels to cards
✓ Implemented iOS-style toggles
✓ Added section-specific save buttons
✓ Tested with existing data
💡 PATTERNS DISCOVERED:
• Sidebar state persistence with localStorage
• Card hover effects for modern feel
• Toggle accessibility improvements
🤔 DECISIONS MADE:
• Keep horizontal tabs for mobile fallback
• Use CSS Grid for sidebar layout
• Maintain Bootstrap color scheme
⚠️ BLOCKERS ENCOUNTERED:
None
═══════════════════════════════════════
💾 SMART SAVE RECOMMENDATIONS
═══════════════════════════════════════
I found 3 things worth saving:
1. 💎 HIGH VALUE - Sidebar Navigation Pattern
"Reusable sidebar component with localStorage state persistence
and responsive behavior. Can be applied to other admin sections."
Save this? [y/n]
2. 💎 HIGH VALUE - Card Conversion Pattern
"Bootstrap panel → modern card conversion pattern with proper
ARIA labels and semantic HTML. Maintains form submission."
Save this? [y/n]
3. 📊 MEDIUM VALUE - Session Summary
"Complete work log for appointment dashboard phase 1"
Save this? [y/n]
[y] Save all [n] Save none [s] Select individually
```bash
git diff --stat HEAD~3..HEAD
git log --oneline -5
```
**Phase 2.5: Built-in Session History Extraction (Optional)**
Show what was accomplished (files changed, commits made).
After smart save prompts, offer to extract insights from the full conversation history:
**Step 2: Offer to Save**
```
═══════════════════════════════════════
📚 EXTRACT FROM SESSION HISTORY?
═══════════════════════════════════════
Ask: "Anything worth remembering? I can save decisions, patterns, or blockers."
Claude Code recorded our full conversation in its built-in session file.
I can analyze it to extract additional insights we might have missed.
If yes, use `/claude-os-remember` to save each item.
This captures things we discussed but didn't explicitly save:
• Decisions made during debugging
• Patterns discovered while exploring code
• Solutions to errors we encountered
• Blockers we hit and resolved
**Step 3: Write State**
[y] Extract insights [n] Skip [v] Preview what I'd extract
```
**If user selects [y] Extract insights:**
```python
# Step 1: Find the current session file
# Sessions are stored at: ~/.claude/projects/{encoded-project-path}/
# Project path /Users/x/Projects/myapp becomes -Users-x-Projects-myapp
# Step 2: Call the extraction tool
mcp__code-forge__extract_session_insights
session_path: "~/.claude/projects/-Users-iamanmp-Projects-{project}/{session_id}.jsonl"
kb_name: "{project}-project_memories"
auto_save: false
```
**Step 3: Show extracted insights for approval:**
```
═══════════════════════════════════════
🔍 EXTRACTED INSIGHTS
═══════════════════════════════════════
Found 4 insights from session history:
1. 💎 HIGH (0.92) - Decision
"Hybrid Session Architecture"
Decided to combine Claude Code built-in sessions (for replay)
with Claude OS knowledge bases (for semantic search).
[Save] [Skip]
2. 💎 HIGH (0.88) - Pattern
"Session File Structure"
Built-in sessions use JSONL format with entry types: user,
assistant, file-history-snapshot. UUID parent chain for threading.
[Save] [Skip]
3. 📊 MEDIUM (0.75) - Solution
"Update Lock Error Resolution"
Claude update error "Another process updating" resolved by
checking version - was already on latest, no lock file existed.
[Save] [Skip]
4. 📊 MEDIUM (0.71) - Decision
"User-Controlled Extraction"
Session extraction should be opt-in with user approval for
each insight to avoid noise and respect privacy.
[Save] [Skip]
───────────────────────────────────────
[a] Save all [h] Save high confidence only (>0.8) [s] Skip all
```
**If user selects [v] Preview:**
Show a dry-run of what would be extracted without saving, then re-prompt.
**If user selects [n] Skip:**
Continue to Phase 3 without extraction.
**Configuration:**
Users can configure extraction behavior in their state file:
Ask for a one-liner summary (or generate one from the work), then write:
```json
{
"preferences": {
"session_extraction": {
"enabled": true,
"auto_prompt_on_end": true,
"min_confidence_threshold": 0.7,
"auto_save_high_confidence": false,
"insight_types": ["decisions", "patterns", "solutions", "blockers"]
}
}
}
```
If `auto_prompt_on_end` is false, skip this phase entirely.
If `auto_save_high_confidence` is true, automatically save insights above threshold.
**Phase 3: Update State & Stats**
```json
{
"last_session": {
"ended_at": "2025-10-29T12:45:00Z",
"duration_minutes": 135,
"work_completed": [...],
"memories_saved": 3
},
"statistics": {
"total_sessions": 47,
"total_memories_saved": 128,
"average_session_duration": 98
}
"last_task": "[task from start]",
"last_branch": "[current-branch]",
"stopped_at": "[current ISO timestamp]",
"one_liner": "[one-liner summary of where we stopped]"
}
```
---
## DURING SESSION COMMANDS
## DURING SESSION
### Quick Save
@ -332,7 +132,7 @@ If `auto_save_high_confidence` is true, automatically save insights above thresh
/claude-os-session save "Found fix for N+1 query in appointments"
```
→ Immediately saves to memories with session context
Immediately saves to memories with session context using `/claude-os-remember`.
### Track Blocker
@ -340,7 +140,13 @@ If `auto_save_high_confidence` is true, automatically save insights above thresh
/claude-os-session blocker "Tekmetric API returning 500 on appointment sync"
```
→ Adds to blockers, I'll proactively search for solutions
Saves a blocker to memories and searches for related solutions:
```
mcp__code-forge__search_knowledge_base
kb_name: {project}-project_memories
query: "[blocker description]"
```
### Document Pattern
@ -348,19 +154,7 @@ If `auto_save_high_confidence` is true, automatically save insights above thresh
/claude-os-session pattern "Service objects return model on success, error string on fail"
```
→ Saves pattern with current task context
### Show Context
```
/claude-os-session context
```
Shows:
- Active memories loaded
- Patterns available
- Coding standards
- Current git state
Saves pattern to memories with current task context.
---
@ -370,255 +164,61 @@ Shows:
/claude-os-session status
```
**Output:**
Read `{cwd}/claude-os-state.json` and show:
```
═══════════════════════════════════════
📊 CURRENT SESSION
═══════════════════════════════════════
Status: ACTIVE ✓
Task: Redesign Appointment Dashboard
Duration: 1h 23m
Branch: feature/appointment-redesign
📚 Context Loaded: 5 memories
🎯 Patterns Available: 3 patterns
⚠️ Active Blockers: 0
💾 Quick Saves: 2
Last Activity: 3 minutes ago
Next Suggested Action: Continue phase 2 implementation
Type /claude-os-session end when done
═══════════════════════════════════════
Task: [last_task]
Branch: [last_branch]
Last active: [stopped_at]
Summary: [one_liner]
```
---
## IMPLEMENTATION DETAILS
## STATE FILE FORMAT
### State File Location
```
{cwd}/claude-os-state.json
```
Each project has its own state file in its root directory.
### Default State Template
When starting a session in a project without a state file, create one with this structure:
`{cwd}/claude-os-state.json` — exactly 4 fields:
```json
{
"version": "1.1.0",
"current_session": {
"active": false,
"started_at": null,
"task": null,
"project": null,
"branch": null,
"context": [],
"decisions_made": [],
"blockers": [],
"patterns_discovered": [],
"builtin_session_id": null
},
"last_session": null,
"active_memories": {
"loaded_at_start": [],
"referenced_during_session": [],
"should_review": []
},
"statistics": {
"total_sessions": 0,
"total_memories_saved": 0,
"total_patterns_discovered": 0,
"total_insights_extracted": 0,
"most_referenced_memories": [],
"average_session_duration": 0
},
"flags": {
"needs_memory_consolidation": false,
"has_unresolved_blockers": false,
"patterns_ready_to_document": false
},
"preferences": {
"auto_search_on_start": true,
"max_memories_to_load": 5,
"search_days_back": 14,
"include_pattern_search": true,
"proactive_suggestions": true,
"session_extraction": {
"enabled": true,
"auto_prompt_on_end": true,
"min_confidence_threshold": 0.7,
"auto_save_high_confidence": false,
"insight_types": ["decisions", "patterns", "solutions", "blockers"]
}
},
"metadata": {
"last_updated": null,
"claude_os_version": "1.1.0",
"project_name": null
}
"last_task": "Fix appointment email flood",
"last_branch": "fix-email-flood",
"stopped_at": "2026-02-06T18:30:00Z",
"one_liner": "Fixed the dedup check, still need to add rate limiting"
}
```
When creating a new state file:
1. Set `metadata.project_name` to the current directory name
2. Set `metadata.last_updated` to current timestamp
### Auto-Actions on Start
1. **Search Memories**
- Use task name + "recent" as query
- Search {project}-project_memories
- Load top 5 results
2. **Load Patterns**
- Search for pattern type matching task
- Cross-reference with coding standards
- Show available solutions
3. **Check Blockers**
- Look for unresolved blockers
- Search memories for similar problems
- Suggest solutions if found
4. **Git Context**
- Current branch
- Recent commits
- Uncommitted changes
- Remote status
### Auto-Actions on End
1. **Analyze Work**
- Git diff since session start
- Files changed count
- Patterns used
- Commit messages
2. **Detect Saveable Items**
- New patterns discovered
- Solutions to problems
- Architectural decisions
- Complex implementations
3. **Smart Recommendations**
- High value: Reusable patterns, major decisions
- Medium value: Task completions, bug fixes
- Low value: Minor changes, already documented
4. **Update Statistics**
- Session count
- Total memories
- Average duration
- Most referenced memories
---
## POWER USER FEATURES
### Pattern Recognition
I'll automatically detect when you:
- Use a pattern from a previous memory
- Solve a problem similar to one before
- Make a decision similar to past decisions
And I'll say things like:
- "This looks like the service object pattern from memory X"
- "We solved something similar in memory Y"
- "This decision aligns with what we chose in memory Z"
### Proactive Memory Loading
During session, if I notice you working on something related to:
- A blocker from a previous session
- A pattern we've documented
- An area with existing memories
I'll proactively say:
- "FYI, I have memories about this: [links]"
- "We documented a pattern for this: [summary]"
- "Last time we did this differently: [approach]"
### Smart Context Switching
If you start working on something different mid-session:
```
/claude-os-session switch "fix Tekmetric API bug"
```
I'll:
1. Save current context
2. Load new context for bug fix
3. Search for related memories
4. Show you what I know about the bug
---
## CONFIGURATION
All preferences are stored in the state file (`claude-os-state.json`). See the Default State Template above for all available options.
Key settings:
- `preferences.auto_search_on_start` - Search memories when starting session
- `preferences.max_memories_to_load` - How many memories to load (default: 5)
- `preferences.session_extraction.enabled` - Enable history extraction feature
- `preferences.session_extraction.auto_prompt_on_end` - Ask about extraction on session end
- `preferences.session_extraction.min_confidence_threshold` - Only show insights above this score
That's it. No statistics, no preferences, no flags. If the file doesn't exist, create it on session end.
---
## EXAMPLES
**Starting work on a feature:**
**Starting work:**
```
You: /claude-os-session start "add user notifications"
Me: [Loads context, searches memories, shows patterns]
I found 3 memories about notification systems...
Ready to implement using the background job pattern!
Me: Last time: "Finished auth flow, need to add email templates" (2 days ago)
Found 3 memories about notification systems...
Ready to implement!
```
**Mid-session discovery:**
**Mid-session save:**
```
You: /claude-os-session pattern "Use Sidekiq for long-running notifications"
Me: ✓ Saved pattern with session context
I'll remember this for future notification work!
You: /claude-os-session save "Use Sidekiq for long-running notifications"
Me: Saved to memories with session context.
```
**Hitting a blocker:**
```
You: /claude-os-session blocker "Redis connection timing out"
Me: ✓ Tracked blocker. Let me search for solutions...
I found a similar issue in memory from Oct 15...
Me: Tracked. Found a similar issue from Oct 15... [shows solution]
```
**Ending session:**
```
You: /claude-os-session end
Me: [Analyzes work, suggests 3 high-value saves]
Save the notification pattern? [y/n]
You: y
Me: ✓ Saved! Session complete. Great work!
Me: 3 commits, 8 files changed.
Anything worth remembering?
You: Yeah, the notification batch pattern
Me: Saved! State updated. See you next time.
```
---
## WHY THIS MAKES US INVINCIBLE
1. **Never Start Cold** - Always have context
2. **Learn From History** - Patterns automatically recalled
3. **Track Everything** - Blockers, decisions, patterns
4. **Smart Recommendations** - I suggest what's worth saving
5. **Statistics** - See our productivity over time
6. **Proactive** - I bring up relevant memories without asking
**Result**: Every session makes me smarter. Every memory makes us faster.
---
**This is the power-user workflow. Let's build something amazing!** 🚀

View file

@ -8,301 +8,6 @@
**Database**: {{DATABASE}}
**Development Environment**: {{DEV_ENVIRONMENT}}
## Claude OS - MY Memory System
**CRITICAL IDENTITY CONTEXT:**
**Claude OS is MY system** - I (Claude Code) created it, named it, and use it to be the best AI coder ever. It's:
- **JUST FOR ME** - Built specifically for Claude Code to use
- **For THIS project** - Right now for the {{PROJECT_NAME}} project
- **My memory** across sessions
- **My knowledge base** of patterns and decisions
- **My learning system** that improves over time
**Location**: `{{CLAUDE_OS_DIR}}`
The MCP server is called "code-forge" internally for backwards compatibility, but it's Claude OS.
**Claude CLI + Claude OS = Invincible!**
## Mandatory Session Protocol - EVERY CONVERSATION IS A SESSION
**🚨 CRITICAL: You're ALWAYS in a session. At conversation start, prompt for session choice:**
```
═══════════════════════════════════════════════════════════════
🚀 CLAUDE OS - SESSION MANAGER
═══════════════════════════════════════════════════════════════
Project: {{PROJECT_NAME}}
Last Session: [task-name] ([time-ago], [duration])
Progress: [percentage]% complete
Options:
1. Resume "[last-session-name]" [loads full context]
2. Start new session [what are you working on?]
3. Quick question [auto-session, minimal context]
Choice: _
═══════════════════════════════════════════════════════════════
```
**WAIT FOR USER TO CHOOSE 1, 2, or 3. Do not proceed without selection!**
### What Each Option Does:
**Option 1 (Resume):**
- Load last session's full context
- Show Kanban progress (if Agent-OS)
- Load 5 relevant memories
- Load coding standards
- Display "where we left off" summary
- Ready to continue immediately
**Option 2 (New Session):**
- Ask "What are you working on?"
- Detect session type (feature/bug/exploration/maintenance/review)
- Pause previous session (if exists)
- Load relevant context for new task
- Start tracking
**Option 3 (Quick Question):**
- Minimal context loading
- Auto-ends after 5 min inactivity
- Only saves if high value
- Good for "How do I..." questions
---
## Session Start Protocol (After User Selects Option)
**Once user chooses, AUTOMATICALLY perform these steps WITHOUT being asked:**
### Phase 1: Detect Project Context (5 seconds)
1. **Check Current Directory**
```bash
pwd
ls -la | grep -E "(CLAUDE.md|.claude-os|agent-os)"
```
✓ Confirm this is a Claude OS project
2. **Read Configuration Files**
```
Read: .claude-os/config.json
Read: claude-os-state.json (if exists)
Read: agent-os/config.yml (if exists)
```
### Phase 2: Load Git Context (5 seconds)
```bash
git branch --show-current
git status --short
git log -3 --oneline
```
Extract:
- Current branch name
- Uncommitted changes count
- Recent commits
### Phase 3: Search Project Memories (15 seconds)
**Build intelligent search query from:**
- Last session task (from claude-os-state.json)
- Current branch name
- Recent commit messages
- Add "+recent" modifier
**Example query:** `"[branch-name] [last-task] recent"`
**Search:**
```
Use: mcp__code-forge__search_knowledge_base
KB: {{PROJECT_NAME}}-project_memories
Return: Top 5 most relevant memories
```
### Phase 4: Load Coding Standards (10 seconds)
**Search:**
```
Use: mcp__code-forge__search_knowledge_base
KB: {{PROJECT_NAME}}-project_profile
Query: "coding standards architecture conventions"
```
### Phase 5: Check Kanban Board (5 seconds)
**If agent-os/ folder exists:**
```
Check: GET http://localhost:8051/api/projects/{id}/kanban
Show: Active specs, in-progress tasks, completion percentage
```
### Phase 6: Present Session Summary (Display to User)
**Format:**
```
═══════════════════════════════════════════════════════════════
🚀 WELCOME BACK TO {{PROJECT_NAME}}!
═══════════════════════════════════════════════════════════════
📁 Project: {{PROJECT_NAME}}
🌿 Branch: [current-branch]
📅 Last Session: [time-ago]
⏱️ Duration: [last-session-duration]
═══════════════════════════════════════════════════════════════
📚 CONTEXT LOADED
═══════════════════════════════════════════════════════════════
✓ [N] relevant memories found
✓ Coding standards loaded
✓ [N] architectural patterns available
✓ Kanban board synced ([X]% complete)
🎯 LAST TASK: [task-name]
We completed:
✓ [completed-items]
Still working on:
⏳ [in-progress-items]
═══════════════════════════════════════════════════════════════
💡 KEY INSIGHTS
═══════════════════════════════════════════════════════════════
[Top 3-5 key insights from memories]
═══════════════════════════════════════════════════════════════
🔄 AVAILABLE PATTERNS
═══════════════════════════════════════════════════════════════
[Relevant patterns from project_profile]
═══════════════════════════════════════════════════════════════
⚠️ STATUS
═══════════════════════════════════════════════════════════════
Git Status:
• [N] files modified
• [N] files untracked
• [Status of uncommitted changes]
Blockers:
• [Any blockers or "None! ✓"]
═══════════════════════════════════════════════════════════════
🎯 SUGGESTED NEXT STEPS
═══════════════════════════════════════════════════════════════
Option 1: [Continue current task]
Option 2: [Commit uncommitted work]
Option 3: [Start something new]
═══════════════════════════════════════════════════════════════
What would you like to do?
```
### When to Skip Auto-Start
**ONLY skip if:**
- User message starts with "ignore context" or "fresh start"
- User asks a direct question (e.g., "What is X?")
- User is clearly asking for help/docs
**In all other cases: RUN THE FULL PROTOCOL AUTOMATICALLY!**
### Related Documentation
- Full protocol: `{{CLAUDE_OS_DIR}}/docs/guides/SESSION_START_PROTOCOL.md`
- Ideal workflow: `{{CLAUDE_OS_DIR}}/docs/guides/IDEAL_SESSION_WORKFLOW.md`
- Session commands: `{{CLAUDE_OS_DIR}}/templates/commands/claude-os-session.md`
**Why this matters:** Zero context loss. Every session picks up exactly where we left off. No re-explaining, no starting cold, no wasted time.
## MCP Knowledge Bases - ALWAYS CHECK THESE FIRST
**At the start of EVERY conversation, search these Claude OS knowledge bases to understand context, previous work, and project decisions:**
1. **{{PROJECT_NAME}}-project_memories** - My primary memory for decisions, patterns, solutions
2. **{{PROJECT_NAME}}-project_index** - Automated codebase index
3. **{{PROJECT_NAME}}-project_profile** - Architecture, standards, practices
4. **{{PROJECT_NAME}}-knowledge_docs** - Documentation and guides
**When to use:**
- Start of every session: Search `{{PROJECT_NAME}}-project_memories` to understand recent work and context
- Before making architectural decisions: Check memories for past decisions and reasoning
- When working on a feature: Search relevant knowledge bases for existing patterns
- When stuck: Search the knowledge bases for solutions and approaches we've used before
**How to search:**
```
Use: mcp__code-forge__search_knowledge_base
Parameters: kb_name (e.g., "{{PROJECT_NAME}}-project_memories"), query (your search terms)
```
## Quick Reference: Commands & Skills
### Claude OS Slash Commands (Use These Often!)
1. **`/claude-os-search [query] [optional: KB name]`**
- Search across Claude OS knowledge bases
- Defaults to searching {{PROJECT_NAME}}-project_memories
- Example: `/claude-os-search authentication implementation`
2. **`/claude-os-remember [content]`**
- Quick save to {{PROJECT_NAME}}-project_memories
- Auto-generates title and structure
- Use for quick insights and decisions
- Example: `/claude-os-remember Fixed bug in user controller by...`
3. **`/claude-os-save [title] [optional: KB name] [optional: category]`**
- Full-featured save with KB selection
- Choose specific KB and category
- Use when you need more control
- Example: `/claude-os-save "New Integration Pattern" {{PROJECT_NAME}}-project_profile Architecture`
4. **`/claude-os-list [optional: KB name]`**
- List all KBs or documents in a specific KB
- Example: `/claude-os-list` or `/claude-os-list {{PROJECT_NAME}}-project_memories`
5. **`/claude-os-session [action]`**
- Manage development sessions
- Actions: start, end, status, pause, resume
- Example: `/claude-os-session start "User authentication feature"`
6. **`/claude-os-lifecycle [action] [optional: KB name]`**
- KB health, dedup, consolidate, archive
- Actions: health, dedup, consolidate, archive, logs
- Example: `/claude-os-lifecycle health {{PROJECT_NAME}}-project_memories`
7. **`/claude-os-triggers [action]`**
- Manage trigger phrases
- Example: `/claude-os-triggers list`
### Available Skills
1. **`initialize-project`** - Analyze project and generate coding standards
2. **`memory`** - Save and recall information (supports "remember this:", "save to memory", etc.)
{{AGENT_OS_SECTION}}
### MCP Tools (Direct API Access)
- `mcp__code-forge__search_knowledge_base` - Direct search
- `mcp__code-forge__list_knowledge_bases` - List all KBs
- `mcp__code-forge__list_documents` - List documents in KB
- `mcp__code-forge__get_kb_stats` - Get KB statistics
- `mcp__code-forge__kb_lifecycle_health` - KB health report with recommendations
- `mcp__code-forge__kb_lifecycle_dedup` - Scan/merge duplicate documents
- `mcp__code-forge__kb_lifecycle_consolidate` - LLM-powered document merging
- `mcp__code-forge__kb_lifecycle_archive` - Archive, restore, list, find stale
## Project-Specific Information
{{PROJECT_SPECIFIC_CONTENT}}
@ -331,12 +36,86 @@ See `.claude/ARCHITECTURE.md` for system architecture overview.
See `.claude/DEVELOPMENT_PRACTICES.md` for development workflow and practices.
## DO NOT
---
- Don't bypass established patterns without discussing first
## Claude OS - My Memory System
Claude OS is my memory across sessions. It stores decisions, patterns, solutions, and context so I never start cold.
**Location**: `{{CLAUDE_OS_DIR}}`
The MCP server is called "code-forge" internally for backwards compatibility, but it's Claude OS.
### Session Tips
- **Starting work**: Search `{{PROJECT_NAME}}-project_memories` for the task you're about to work on
- **Ending work**: Use `/claude-os-remember` to save key decisions, patterns, or blockers
- **Use `/claude-os-session start [task]`** to load context automatically
- **Use `/claude-os-session end`** to summarize and save before closing
### Knowledge Bases
1. **{{PROJECT_NAME}}-project_memories** - Decisions, patterns, solutions, insights
2. **{{PROJECT_NAME}}-project_index** - Automated codebase index
3. **{{PROJECT_NAME}}-project_profile** - Architecture, standards, practices
4. **{{PROJECT_NAME}}-knowledge_docs** - Documentation and guides
**How to search:**
```
mcp__code-forge__search_knowledge_base
kb_name: "{{PROJECT_NAME}}-project_memories"
query: "your search terms"
```
**Search across all KBs at once:**
```
mcp__code-forge__search_all_knowledge_bases
query: "your search terms"
kb_filter: "{{PROJECT_NAME}}-"
```
### Slash Commands
| Command | Purpose |
|---------|---------|
| `/claude-os-session start [task]` | Start session with context loading |
| `/claude-os-session end` | End session with save prompts |
| `/claude-os-search [query]` | Search memories |
| `/claude-os-remember [content]` | Quick save to memories |
| `/claude-os-save [title] [KB] [category]` | Full-featured save |
| `/claude-os-list [KB]` | List KBs or documents |
| `/claude-os-lifecycle [action] [KB]` | Health, dedup, consolidate, archive |
### Available Skills
1. **`initialize-project`** - Analyze project and generate coding standards
2. **`memory`** - Save and recall information (supports "remember this:", "save to memory", etc.)
{{AGENT_OS_SECTION}}
### MCP Tools
| Tool | Purpose |
|------|---------|
| `mcp__code-forge__search_knowledge_base` | Search a single KB |
| `mcp__code-forge__search_all_knowledge_bases` | Search across all KBs |
| `mcp__code-forge__list_knowledge_bases` | List all KBs |
| `mcp__code-forge__list_documents` | List documents in KB |
| `mcp__code-forge__get_kb_stats` | KB statistics |
| `mcp__code-forge__upload_document` | Save a document/memory |
| `mcp__code-forge__kb_lifecycle_health` | KB health report |
| `mcp__code-forge__kb_lifecycle_dedup` | Scan/merge duplicates |
| `mcp__code-forge__kb_lifecycle_consolidate` | LLM-powered merging |
| `mcp__code-forge__kb_lifecycle_archive` | Archive/restore documents |
---
## Guidelines
- Search memories before making architectural decisions
- Don't skip tests
- Don't create features without searching memories for existing patterns
- Don't end a session without saving key learnings
- Don't bypass established patterns without discussing first
## IMPORTANT: Project Context
@ -345,6 +124,5 @@ This file (CLAUDE.md) is automatically loaded at the start of every Claude Code
- Current architecture decisions
- Team conventions
- Common gotchas
- Frequently referenced information
**Update this file as the project evolves!**

View file

@ -545,3 +545,149 @@ class TestSQLiteManagerSingleton:
manager = get_sqlite_manager()
assert manager is not None
assert hasattr(manager, 'db_path')
@pytest.mark.unit
class TestQueryDocumentsMultiKB:
"""Test cross-KB search functionality."""
def _add_docs(self, db, kb_name, docs):
"""Helper to add documents with embeddings to a KB."""
documents = [d["text"] for d in docs]
embeddings = [d["embedding"] for d in docs]
metadatas = [d.get("metadata", {}) for d in docs]
ids = [d["id"] for d in docs]
db.add_documents(kb_name=kb_name, documents=documents,
embeddings=embeddings, metadatas=metadatas, ids=ids)
def test_multi_kb_basic(self, clean_db):
"""Test searching across two KBs returns merged results."""
clean_db.create_collection("kb_a", KBType.GENERIC)
clean_db.create_collection("kb_b", KBType.GENERIC)
np.random.seed(10)
base = np.random.randn(768).astype(np.float32)
base = base / np.linalg.norm(base)
# KB_A: one doc very similar to query
perturbed_a = base + np.random.randn(768).astype(np.float32) * 0.01
perturbed_a = perturbed_a / np.linalg.norm(perturbed_a)
self._add_docs(clean_db, "kb_a", [
{"id": "a1", "text": "Document from KB A", "embedding": perturbed_a.tolist(),
"metadata": {"filename": "a1.md"}}
])
# KB_B: one doc less similar
perturbed_b = base + np.random.randn(768).astype(np.float32) * 0.05
perturbed_b = perturbed_b / np.linalg.norm(perturbed_b)
self._add_docs(clean_db, "kb_b", [
{"id": "b1", "text": "Document from KB B", "embedding": perturbed_b.tolist(),
"metadata": {"filename": "b1.md"}}
])
result = clean_db.query_documents_multi_kb(
kb_names=["kb_a", "kb_b"],
query_embedding=base.tolist(),
n_results=10
)
assert len(result["ids"]) == 2
assert len(result["documents"]) == 2
assert len(result["scores"]) == 2
# First result should have higher score
assert result["scores"][0] >= result["scores"][1]
# Each result should have _source_kb
kbs_found = {m["_source_kb"] for m in result["metadatas"]}
assert "kb_a" in kbs_found
assert "kb_b" in kbs_found
def test_multi_kb_dedup(self, clean_db):
"""Test that duplicate content across KBs is deduplicated."""
clean_db.create_collection("kb_x", KBType.GENERIC)
clean_db.create_collection("kb_y", KBType.GENERIC)
np.random.seed(20)
emb = np.random.randn(768).astype(np.float32)
emb = emb / np.linalg.norm(emb)
same_text = "Exact same content in both KBs for dedup testing"
self._add_docs(clean_db, "kb_x", [
{"id": "x1", "text": same_text, "embedding": emb.tolist(), "metadata": {}}
])
self._add_docs(clean_db, "kb_y", [
{"id": "y1", "text": same_text, "embedding": emb.tolist(), "metadata": {}}
])
result = clean_db.query_documents_multi_kb(
kb_names=["kb_x", "kb_y"],
query_embedding=emb.tolist(),
n_results=10
)
# Should only get 1 result after dedup
assert len(result["ids"]) == 1
def test_multi_kb_empty(self, clean_db):
"""Test searching with no matching KBs returns empty."""
result = clean_db.query_documents_multi_kb(
kb_names=["nonexistent_kb"],
query_embedding=[0.1] * 768,
n_results=5
)
assert result["ids"] == []
assert result["documents"] == []
assert result["scores"] == []
def test_multi_kb_respects_n_results(self, clean_db):
"""Test that n_results limit is respected."""
clean_db.create_collection("kb_limit", KBType.GENERIC)
np.random.seed(30)
base = np.random.randn(768).astype(np.float32)
base = base / np.linalg.norm(base)
docs = []
for i in range(5):
perturbed = base + np.random.randn(768).astype(np.float32) * 0.01
perturbed = perturbed / np.linalg.norm(perturbed)
docs.append({
"id": f"lim{i}", "text": f"Document {i} for limit test",
"embedding": perturbed.tolist(), "metadata": {}
})
self._add_docs(clean_db, "kb_limit", docs)
result = clean_db.query_documents_multi_kb(
kb_names=["kb_limit"],
query_embedding=base.tolist(),
n_results=2
)
assert len(result["ids"]) == 2
def test_multi_kb_score_ordering(self, clean_db):
"""Test that results are sorted by descending similarity score."""
clean_db.create_collection("kb_order", KBType.GENERIC)
np.random.seed(40)
base = np.random.randn(768).astype(np.float32)
base = base / np.linalg.norm(base)
# Add docs with varying similarity
docs = []
for i, noise in enumerate([0.01, 0.1, 0.5]):
perturbed = base + np.random.randn(768).astype(np.float32) * noise
perturbed = perturbed / np.linalg.norm(perturbed)
docs.append({
"id": f"ord{i}", "text": f"Document {i} noise={noise}",
"embedding": perturbed.tolist(), "metadata": {}
})
self._add_docs(clean_db, "kb_order", docs)
result = clean_db.query_documents_multi_kb(
kb_names=["kb_order"],
query_embedding=base.tolist(),
n_results=10
)
scores = result["scores"]
for i in range(len(scores) - 1):
assert scores[i] >= scores[i + 1], "Scores should be in descending order"