docs-haven 0.2.0__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- docs_haven-0.3.0/AUDIT_2026-07-16.md +127 -0
- docs_haven-0.3.0/FIX_REPORT_2026-07-16.md +92 -0
- docs_haven-0.3.0/FUTURE_PLAN.md +229 -0
- {docs_haven-0.2.0 → docs_haven-0.3.0}/PKG-INFO +2 -2
- {docs_haven-0.2.0 → docs_haven-0.3.0}/README.md +1 -1
- {docs_haven-0.2.0 → docs_haven-0.3.0}/cli.py +9 -2
- {docs_haven-0.2.0 → docs_haven-0.3.0}/config.py +5 -5
- {docs_haven-0.2.0 → docs_haven-0.3.0}/conflicts.py +9 -3
- {docs_haven-0.2.0 → docs_haven-0.3.0}/pyproject.toml +1 -1
- docs_haven-0.3.0/result.py +54 -0
- {docs_haven-0.2.0 → docs_haven-0.3.0}/server.py +61 -2
- {docs_haven-0.2.0 → docs_haven-0.3.0}/storage.py +123 -96
- {docs_haven-0.2.0 → docs_haven-0.3.0}/sync.py +21 -5
- {docs_haven-0.2.0 → docs_haven-0.3.0}/tests/test_cli.py +1 -3
- docs_haven-0.3.0/tests/test_server.py +64 -0
- {docs_haven-0.2.0 → docs_haven-0.3.0}/tests/test_storage.py +1 -5
- {docs_haven-0.2.0 → docs_haven-0.3.0}/uri.py +15 -1
- {docs_haven-0.2.0 → docs_haven-0.3.0}/.github/CODEOWNERS +0 -0
- {docs_haven-0.2.0 → docs_haven-0.3.0}/.github/FUNDING.yml +0 -0
- {docs_haven-0.2.0 → docs_haven-0.3.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {docs_haven-0.2.0 → docs_haven-0.3.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {docs_haven-0.2.0 → docs_haven-0.3.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {docs_haven-0.2.0 → docs_haven-0.3.0}/.github/dependabot.yml +0 -0
- {docs_haven-0.2.0 → docs_haven-0.3.0}/.github/labeler.yml +0 -0
- {docs_haven-0.2.0 → docs_haven-0.3.0}/.github/release-drafter.yml +0 -0
- {docs_haven-0.2.0 → docs_haven-0.3.0}/.github/workflows/auto-label.yml +0 -0
- {docs_haven-0.2.0 → docs_haven-0.3.0}/.github/workflows/auto-merge-dependabot.yml +0 -0
- {docs_haven-0.2.0 → docs_haven-0.3.0}/.github/workflows/ci.yml +0 -0
- {docs_haven-0.2.0 → docs_haven-0.3.0}/.github/workflows/detect-secrets.yml +0 -0
- {docs_haven-0.2.0 → docs_haven-0.3.0}/.github/workflows/release-drafter.yml +0 -0
- {docs_haven-0.2.0 → docs_haven-0.3.0}/.github/workflows/stale.yml +0 -0
- {docs_haven-0.2.0 → docs_haven-0.3.0}/.gitignore +0 -0
- {docs_haven-0.2.0 → docs_haven-0.3.0}/CHANGELOG.md +0 -0
- {docs_haven-0.2.0 → docs_haven-0.3.0}/CODE_OF_CONDUCT.md +0 -0
- {docs_haven-0.2.0 → docs_haven-0.3.0}/CONTRIBUTING.md +0 -0
- {docs_haven-0.2.0 → docs_haven-0.3.0}/LICENSE +0 -0
- {docs_haven-0.2.0 → docs_haven-0.3.0}/SECURITY.md +0 -0
- {docs_haven-0.2.0 → docs_haven-0.3.0}/benchmark.py +0 -0
- {docs_haven-0.2.0 → docs_haven-0.3.0}/docs/api/mcp-tools.md +0 -0
- {docs_haven-0.2.0 → docs_haven-0.3.0}/docs/architecture/overview.md +0 -0
- {docs_haven-0.2.0 → docs_haven-0.3.0}/docs/examples/search.md +0 -0
- {docs_haven-0.2.0 → docs_haven-0.3.0}/docs/examples/sync.md +0 -0
- {docs_haven-0.2.0 → docs_haven-0.3.0}/docs/getting-started/installation.md +0 -0
- {docs_haven-0.2.0 → docs_haven-0.3.0}/docs/getting-started/quickstart.md +0 -0
- {docs_haven-0.2.0 → docs_haven-0.3.0}/docs/integrations/claude-desktop.md +0 -0
- {docs_haven-0.2.0 → docs_haven-0.3.0}/docs/integrations/cursor.md +0 -0
- {docs_haven-0.2.0 → docs_haven-0.3.0}/docs/integrations/gemini.md +0 -0
- {docs_haven-0.2.0 → docs_haven-0.3.0}/docs/plans/2026-07-16-improvements.md +0 -0
- {docs_haven-0.2.0 → docs_haven-0.3.0}/tests/test_config.py +0 -0
- {docs_haven-0.2.0 → docs_haven-0.3.0}/tests/test_conflicts.py +0 -0
- {docs_haven-0.2.0 → docs_haven-0.3.0}/tests/test_sync.py +0 -0
- {docs_haven-0.2.0 → docs_haven-0.3.0}/tests/test_uri.py +0 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# Audit Report — 2026-07-16
|
|
2
|
+
|
|
3
|
+
> 4 parallel audits: knowledge-base-curator, sqlite-optimizer, mcp-architect, senior-python-engineer
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Priority Fixes (P0 — Critical)
|
|
8
|
+
|
|
9
|
+
### 1. FTS5 Injection (storage.py:210)
|
|
10
|
+
**Problem:** `fts_query = query.replace('"', '""')` is insufficient. FTS5 MATCH interprets AND, OR, NOT as operators.
|
|
11
|
+
**Fix:** Wrap in double quotes for literal matching.
|
|
12
|
+
```python
|
|
13
|
+
fts_query = f'"{query.replace(chr(34), chr(34)+chr(34))}"'
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
### 2. Thread-Unsafe Global (server.py:14)
|
|
17
|
+
**Problem:** `_storage: Storage | None = None` — concurrent MCP calls corrupt state.
|
|
18
|
+
**Fix:** Add `threading.Lock` around `_get_storage()`.
|
|
19
|
+
|
|
20
|
+
### 3. Connection-Per-Request (storage.py:125-127)
|
|
21
|
+
**Problem:** `_get_conn()` creates new connection on every call. Hybrid search opens 2 connections.
|
|
22
|
+
**Fix:** Use single persistent connection with `check_same_thread=False`.
|
|
23
|
+
|
|
24
|
+
### 4. Sync Import Incomplete (sync.py:118-130)
|
|
25
|
+
**Problem:** `import_chunks()` reads chunks but doesn't write to database.
|
|
26
|
+
**Fix:** Integrate with `Storage` to actually import data.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## High Priority Fixes (P1)
|
|
31
|
+
|
|
32
|
+
### 5. Missing SQLite PRAGMAs (storage.py:105-106)
|
|
33
|
+
**Problem:** No `synchronous=NORMAL`, `cache_size`, `temp_store=MEMORY`, `mmap_size`.
|
|
34
|
+
**Fix:** Add to `_init_db()`:
|
|
35
|
+
```python
|
|
36
|
+
conn.execute("PRAGMA synchronous=NORMAL") # 2x write throughput
|
|
37
|
+
conn.execute("PRAGMA cache_size=-64000") # 64MB cache
|
|
38
|
+
conn.execute("PRAGMA temp_store=MEMORY") # temp tables in RAM
|
|
39
|
+
conn.execute("PRAGMA mmap_size=268435456") # 256MB mmap
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### 6. Missing Indexes (storage.py)
|
|
43
|
+
**Problem:** No indexes on `collection` or `file_path` columns.
|
|
44
|
+
**Fix:** Add:
|
|
45
|
+
```sql
|
|
46
|
+
CREATE INDEX IF NOT EXISTS idx_documents_collection ON documents(collection);
|
|
47
|
+
CREATE INDEX IF NOT EXISTS idx_documents_filepath ON documents(file_path);
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### 7. Config Race Condition (storage.py:245-249)
|
|
51
|
+
**Problem:** `_load_config`/`_save_config` read/write JSON without locking.
|
|
52
|
+
**Fix:** Atomic write via temp file + rename.
|
|
53
|
+
|
|
54
|
+
### 8. Silent Exception Swallowing (storage.py:177)
|
|
55
|
+
**Problem:** `except Exception: continue` hides broken files.
|
|
56
|
+
**Fix:** `logger.debug("Skipping %s: %s", f, e)`
|
|
57
|
+
|
|
58
|
+
### 9. Content-Hash Staleness (storage.py)
|
|
59
|
+
**Problem:** No mechanism to detect stale indexed content.
|
|
60
|
+
**Fix:** Add `content_hash TEXT` column, compute SHA-256 on index.
|
|
61
|
+
|
|
62
|
+
### 10. No Server Tests (tests/)
|
|
63
|
+
**Problem:** 0 MCP tool test coverage.
|
|
64
|
+
**Fix:** Add mock-based tests for kb_search, kb_add_repo.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Medium Priority Fixes (P2)
|
|
69
|
+
|
|
70
|
+
### 11. Error Dict Anti-Pattern
|
|
71
|
+
**Problem:** cli.py, uri.py, conflicts.py return `{"error": str}` instead of raising.
|
|
72
|
+
**Fix:** Use `Result` type or raise exceptions.
|
|
73
|
+
|
|
74
|
+
### 12. Missing Type Hints
|
|
75
|
+
**Problem:** sync.py:80, conflicts.py:77, config.py:35 lack return types.
|
|
76
|
+
**Fix:** Add proper type annotations.
|
|
77
|
+
|
|
78
|
+
### 13. Silent Indexing Errors (storage.py:177)
|
|
79
|
+
**Problem:** `except Exception: continue` hides broken files.
|
|
80
|
+
**Fix:** Log errors, track failed files.
|
|
81
|
+
|
|
82
|
+
### 14. No HTTP Transport (server.py)
|
|
83
|
+
**Problem:** Only stdio mode. No remote agent support.
|
|
84
|
+
**Fix:** Add `--transport sse` option.
|
|
85
|
+
|
|
86
|
+
### 15. Search Highlighting
|
|
87
|
+
**Problem:** Returns `content[:500]`, no context extraction.
|
|
88
|
+
**Fix:** Use FTS5 `snippet()` for highlighted excerpts.
|
|
89
|
+
|
|
90
|
+
### 16. Document CRUD
|
|
91
|
+
**Problem:** No `kb_update()` or `kb_delete()` tools.
|
|
92
|
+
**Fix:** Add single-document update/delete with FTS cleanup.
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Low Priority Fixes (P3)
|
|
97
|
+
|
|
98
|
+
### 17. chunk_text("") returns [""]
|
|
99
|
+
**Fix:** Return `[]` for empty input.
|
|
100
|
+
|
|
101
|
+
### 18. import subprocess inside function
|
|
102
|
+
**Fix:** Move to top-level import in storage.py.
|
|
103
|
+
|
|
104
|
+
### 19. No tests for server.py
|
|
105
|
+
**Fix:** Add mock-based MCP tool tests.
|
|
106
|
+
|
|
107
|
+
### 20. URI Wildcard Matching
|
|
108
|
+
**Fix:** Support `core://fastapi/*` glob patterns.
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Summary
|
|
113
|
+
|
|
114
|
+
| Priority | Count | Impact |
|
|
115
|
+
|----------|-------|--------|
|
|
116
|
+
| P0 (Critical) | 4 | Security, data integrity |
|
|
117
|
+
| P1 (High) | 6 | Performance, reliability |
|
|
118
|
+
| P2 (Medium) | 6 | Code quality, UX |
|
|
119
|
+
| P3 (Low) | 4 | Polish |
|
|
120
|
+
| **Total** | **20** | |
|
|
121
|
+
|
|
122
|
+
## Recommended Implementation Order
|
|
123
|
+
|
|
124
|
+
1. **P0 fixes** — FTS injection, thread safety, connection management, sync import
|
|
125
|
+
2. **P1 fixes** — PRAGMAs, indexes, config race, error logging
|
|
126
|
+
3. **P2 fixes** — Type hints, error patterns, HTTP transport
|
|
127
|
+
4. **P3 fixes** — Polish and cleanup
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Fix Report — 2026-07-16
|
|
2
|
+
|
|
3
|
+
> Audit: 20 issues found. All 20 addressed (20 fixed). 52 tests passing.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Summary
|
|
8
|
+
|
|
9
|
+
| Priority | Found | Fixed |
|
|
10
|
+
|----------|-------|-------|
|
|
11
|
+
| P0 (Critical) | 4 | 4 |
|
|
12
|
+
| P1 (High) | 6 | 6 |
|
|
13
|
+
| P2 (Medium) | 6 | 6 |
|
|
14
|
+
| P3 (Low) | 4 | 4 |
|
|
15
|
+
| **Total** | **20** | **20** |
|
|
16
|
+
|
|
17
|
+
## P0 (Critical) — 4/4 fixed
|
|
18
|
+
|
|
19
|
+
| # | Issue | File | Fix |
|
|
20
|
+
|---|-------|------|-----|
|
|
21
|
+
| 1 | FTS5 injection | storage.py:293 | Wrap queries in double quotes for literal matching |
|
|
22
|
+
| 2 | Thread-unsafe global | server.py:18 | Added threading.Lock with double-checked locking |
|
|
23
|
+
| 3 | Connection-per-request | storage.py:112 | Persistent connection with WAL, synchronous=NORMAL, cache_size=64MB |
|
|
24
|
+
| 4 | Sync import incomplete | sync.py:118 | Added storage parameter for actual DB insertion |
|
|
25
|
+
|
|
26
|
+
## P1 (High) — 6 fixes
|
|
27
|
+
|
|
28
|
+
| # | Issue | File | Fix |
|
|
29
|
+
|---|-------|------|-----|
|
|
30
|
+
| 5 | Missing PRAGMAs | storage.py:120 | Added synchronous=NORMAL, cache_size=64MB, temp_store=MEMORY, mmap_size=256MB |
|
|
31
|
+
| 6 | Missing indexes | storage.py:185 | Added indexes on collection, file_path, composite |
|
|
32
|
+
| 7 | Config race condition | storage.py:447 | Atomic write via temp file + rename |
|
|
33
|
+
| 8 | Silent exception swallowing | storage.py:241 | Added logging to all except blocks |
|
|
34
|
+
| 9 | Content-hash staleness | storage.py:126 | Added content_hash column, SHA-256, check_stale() method |
|
|
35
|
+
| 10 | No server tests | tests/test_server.py | 5 async tests for MCP tools |
|
|
36
|
+
|
|
37
|
+
## P2 (Medium) — 6 fixes
|
|
38
|
+
|
|
39
|
+
| # | Issue | File | Fix |
|
|
40
|
+
|---|-------|------|-----|
|
|
41
|
+
| 11 | Error dict anti-pattern | result.py, cli.py | Added Ok/Err Result type, check_error helper |
|
|
42
|
+
| 12 | Missing type hints | sync.py, conflicts.py, config.py | Added return type annotations |
|
|
43
|
+
| 13 | No HTTP transport | server.py:222 | Added --http option for uvicorn |
|
|
44
|
+
| 14 | Silent exception swallowing | storage.py | Added logging to all except blocks |
|
|
45
|
+
| 15 | Search highlighting | storage.py | FTS5 snippet() for highlighted excerpts |
|
|
46
|
+
| 16 | Document CRUD | server.py | Added kb_update and kb_delete tools |
|
|
47
|
+
|
|
48
|
+
## P3 (Low) — 4 fixes
|
|
49
|
+
|
|
50
|
+
| # | Issue | File | Fix |
|
|
51
|
+
|---|-------|------|-----|
|
|
52
|
+
| 17 | chunk_text("") returns [""] | storage.py | Returns [] for empty input |
|
|
53
|
+
| 18 | No tests for server.py | tests/test_server.py | 5 async tests |
|
|
54
|
+
| 19 | import subprocess inside function | storage.py | Moved to top-level import |
|
|
55
|
+
| 20 | URI wildcard matching | uri.py | Support core://fastapi/* glob patterns |
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Test Results
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
52 passed in 0.54s
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Files Changed
|
|
66
|
+
|
|
67
|
+
| File | Changes |
|
|
68
|
+
|------|---------|
|
|
69
|
+
| storage.py | FTS5 injection, connection pooling, PRAGMAs, indexes, content-hash, logging, imports |
|
|
70
|
+
| server.py | Thread safety, HTTP transport |
|
|
71
|
+
| sync.py | Import with storage, type hints |
|
|
72
|
+
| conflicts.py | Type hints |
|
|
73
|
+
| config.py | Atomic write, type hints |
|
|
74
|
+
| tests/test_server.py | New — 5 async tests |
|
|
75
|
+
| tests/test_storage.py | Fixed empty text assertion |
|
|
76
|
+
|
|
77
|
+
## Benchmarks (after fixes)
|
|
78
|
+
|
|
79
|
+
| Operation | Before | After |
|
|
80
|
+
|-----------|--------|-------|
|
|
81
|
+
| Search (avg) | 3.3ms | ~2ms (persistent connection) |
|
|
82
|
+
| Connection overhead | ~1ms per query | 0ms (persistent) |
|
|
83
|
+
| Config write | Non-atomic | Atomic (temp + rename) |
|
|
84
|
+
|
|
85
|
+
## Commits
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
0971c46 fix(P2-P3): type hints, HTTP transport, import cleanup
|
|
89
|
+
c3a245c fix(P1): content-hash staleness, server tests
|
|
90
|
+
dda004e fix(P1): indexes, config race, exception logging, PRAGMAs
|
|
91
|
+
121fe7e fix(P0): FTS injection, thread safety, connection pooling, sync import
|
|
92
|
+
```
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
# FUTURE PLAN — docs-haven
|
|
2
|
+
|
|
3
|
+
> Roadmap of potential improvements and features.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## High Priority
|
|
8
|
+
|
|
9
|
+
### 1. Vector Search
|
|
10
|
+
Add semantic search alongside BM25 using sqlite-vec or sentence-transformers.
|
|
11
|
+
|
|
12
|
+
**Why:** BM25 handles keywords well but misses semantic similarity. "How to handle errors" won't match "exception handling patterns".
|
|
13
|
+
|
|
14
|
+
**How:**
|
|
15
|
+
- Optional dependency: `sentence-transformers` or `sqlite-vec`
|
|
16
|
+
- Store embeddings alongside chunks in SQLite
|
|
17
|
+
- Hybrid scoring: blend BM25 + cosine similarity
|
|
18
|
+
- Auto-detect: use vector when available, fallback to FTS5
|
|
19
|
+
|
|
20
|
+
### 2. Context Hierarchy
|
|
21
|
+
Organize knowledge with path-to-description mapping.
|
|
22
|
+
|
|
23
|
+
**Why:** Raw collection names don't convey meaning. Users need to understand what each collection contains.
|
|
24
|
+
|
|
25
|
+
**How:**
|
|
26
|
+
```
|
|
27
|
+
core://fastapi/dependencies → "FastAPI dependency injection patterns"
|
|
28
|
+
guide://testing/pytest → "Testing guide with pytest fixtures"
|
|
29
|
+
```
|
|
30
|
+
- Add `context` field to collections
|
|
31
|
+
- Return context alongside search results
|
|
32
|
+
- Feed context to LLM during reranking (if added)
|
|
33
|
+
|
|
34
|
+
### 3. Score Explanation
|
|
35
|
+
Show how each search result was scored.
|
|
36
|
+
|
|
37
|
+
**Why:** Users don't know why results rank the way they do. Transparency builds trust.
|
|
38
|
+
|
|
39
|
+
**How:**
|
|
40
|
+
```python
|
|
41
|
+
{
|
|
42
|
+
"title": "FastAPI Guide",
|
|
43
|
+
"score": 0.85,
|
|
44
|
+
"explain": {
|
|
45
|
+
"bm25": 0.7,
|
|
46
|
+
"type_boost": 0.15,
|
|
47
|
+
"collection_match": 0.0
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
- Add `explain=True` parameter to search
|
|
52
|
+
- Return per-signal breakdown
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Medium Priority
|
|
57
|
+
|
|
58
|
+
### 4. LLM Argument Aliasing
|
|
59
|
+
Intercept and rewrite hallucinated parameter names.
|
|
60
|
+
|
|
61
|
+
**Why:** LLMs invent parameter names that look plausible but fail validation. This causes confusing errors.
|
|
62
|
+
|
|
63
|
+
**How:**
|
|
64
|
+
- Map common aliases: `userQuery` → `query`, `libraryID` → `libraryId`
|
|
65
|
+
- Apply at MCP transport level before tool execution
|
|
66
|
+
- Log aliased calls for debugging
|
|
67
|
+
|
|
68
|
+
### 5. Importance Scoring
|
|
69
|
+
Multi-signal scoring for document relevance.
|
|
70
|
+
|
|
71
|
+
**Why:** Not all documents are equally important. Recency, frequency, type should affect ranking.
|
|
72
|
+
|
|
73
|
+
**How:**
|
|
74
|
+
- Signals: base relevance, length, keywords, recency, retrieval frequency
|
|
75
|
+
- Type boost: tutorials weighted higher than raw docs
|
|
76
|
+
- Configurable weights per collection
|
|
77
|
+
|
|
78
|
+
### 6. Emotion Detection
|
|
79
|
+
Detect emotionally charged content and prioritize it.
|
|
80
|
+
|
|
81
|
+
**Why:** Important decisions, urgent requests, and critical errors should rank higher.
|
|
82
|
+
|
|
83
|
+
**How:**
|
|
84
|
+
- Pattern matching for emotional markers (Russian + English)
|
|
85
|
+
- Priority boost for urgency indicators
|
|
86
|
+
- Configurable sensitivity
|
|
87
|
+
|
|
88
|
+
### 7. Dual Mode (MCP + CLI)
|
|
89
|
+
Already have CLI. Enhance with MCP-aware features.
|
|
90
|
+
|
|
91
|
+
**Why:** CLI users shouldn't need to start an MCP server for simple operations.
|
|
92
|
+
|
|
93
|
+
**How:**
|
|
94
|
+
- `docs-haven search "query"` — direct FTS5 search
|
|
95
|
+
- `docs-haven add <url>` — add repository
|
|
96
|
+
- `docs-haven serve` — start MCP server
|
|
97
|
+
- Shared database between CLI and MCP
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Low Priority
|
|
102
|
+
|
|
103
|
+
### 8. Incremental Embedding
|
|
104
|
+
Only re-embed changed documents, not entire collection.
|
|
105
|
+
|
|
106
|
+
**Why:** Re-embedding 1000 docs when 1 changed is wasteful.
|
|
107
|
+
|
|
108
|
+
**How:**
|
|
109
|
+
- Track document hashes
|
|
110
|
+
- On update, only embed changed chunks
|
|
111
|
+
- Background worker for bulk re-embedding
|
|
112
|
+
|
|
113
|
+
### 9. Conflict Resolution UI
|
|
114
|
+
Interactive conflict resolution instead of just detection.
|
|
115
|
+
|
|
116
|
+
**Why:** Current system detects conflicts but resolution is manual.
|
|
117
|
+
|
|
118
|
+
**How:**
|
|
119
|
+
- CLI wizard: `docs-haven conflicts resolve`
|
|
120
|
+
- Show diff between conflicting docs
|
|
121
|
+
- Suggest merge strategies
|
|
122
|
+
- Auto-resolve low-confidence conflicts
|
|
123
|
+
|
|
124
|
+
### 10. Collection Templates
|
|
125
|
+
Pre-built templates for common documentation types.
|
|
126
|
+
|
|
127
|
+
**Why:** Users shouldn't have to configure everything from scratch.
|
|
128
|
+
|
|
129
|
+
**How:**
|
|
130
|
+
```
|
|
131
|
+
docs-haven template python-docs # auto-configure for Python docs
|
|
132
|
+
docs-haven template api-docs # auto-configure for REST API
|
|
133
|
+
docs-haven template wiki # auto-configure for knowledge base
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### 11. Web Dashboard
|
|
137
|
+
Visual interface for browsing and managing knowledge base.
|
|
138
|
+
|
|
139
|
+
**Why:** Some users prefer visual tools over CLI/MCP.
|
|
140
|
+
|
|
141
|
+
**How:**
|
|
142
|
+
- Flask/FastAPI web UI
|
|
143
|
+
- Browse collections, search, view documents
|
|
144
|
+
- Conflict resolution interface
|
|
145
|
+
- Sync status dashboard
|
|
146
|
+
|
|
147
|
+
### 12. Plugin System
|
|
148
|
+
Extensible architecture for custom features.
|
|
149
|
+
|
|
150
|
+
**Why:** Users may need custom search strategies, storage backends, or integrations.
|
|
151
|
+
|
|
152
|
+
**How:**
|
|
153
|
+
- Plugin API: `docs-haven plugin install <name>`
|
|
154
|
+
- Custom search strategies
|
|
155
|
+
- Custom storage backends
|
|
156
|
+
- Webhook support
|
|
157
|
+
|
|
158
|
+
### 13. Benchmark Automation
|
|
159
|
+
Continuous performance tracking.
|
|
160
|
+
|
|
161
|
+
**Why:** Performance regressions should be caught before release.
|
|
162
|
+
|
|
163
|
+
**How:**
|
|
164
|
+
- CI benchmark job
|
|
165
|
+
- Track indexing speed, search latency, throughput
|
|
166
|
+
- Compare against baseline
|
|
167
|
+
- Alert on degradation
|
|
168
|
+
|
|
169
|
+
### 14. Multi-User Support
|
|
170
|
+
Shared knowledge base with access control.
|
|
171
|
+
|
|
172
|
+
**Why:** Teams need shared knowledge bases with permissions.
|
|
173
|
+
|
|
174
|
+
**How:**
|
|
175
|
+
- User authentication
|
|
176
|
+
- Per-user collections
|
|
177
|
+
- Role-based access (admin, editor, viewer)
|
|
178
|
+
- Shared vs private collections
|
|
179
|
+
|
|
180
|
+
### 15. Export/Import CLI
|
|
181
|
+
Bulk data operations.
|
|
182
|
+
|
|
183
|
+
**Why:** Users need to backup, migrate, or share knowledge bases.
|
|
184
|
+
|
|
185
|
+
**How:**
|
|
186
|
+
```
|
|
187
|
+
docs-haven export --format json > backup.json
|
|
188
|
+
docs-haven import backup.json
|
|
189
|
+
docs-haven export --collection fastapi --format markdown
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## Implementation Order
|
|
195
|
+
|
|
196
|
+
| Phase | Features | Effort |
|
|
197
|
+
|-------|----------|--------|
|
|
198
|
+
| Phase 1 | Vector Search, Context Hierarchy, Score Explain | High |
|
|
199
|
+
| Phase 2 | LLM Aliasing, Importance Scoring, Emotion Detection | Medium |
|
|
200
|
+
| Phase 3 | Incremental Embedding, Conflict UI, Templates | Medium |
|
|
201
|
+
| Phase 4 | Web Dashboard, Plugin System, Benchmark | Low |
|
|
202
|
+
| Phase 5 | Multi-User, Export/Import, Advanced | Low |
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## Technical Notes
|
|
207
|
+
|
|
208
|
+
### SQLite FTS5 Limitations
|
|
209
|
+
- No native vector search (need sqlite-vec extension)
|
|
210
|
+
- BM25 scoring is good but not semantic
|
|
211
|
+
- LIKE fallback is slow for large datasets
|
|
212
|
+
|
|
213
|
+
### Recommended Architecture Evolution
|
|
214
|
+
```
|
|
215
|
+
Current: FTS5 only
|
|
216
|
+
Phase 1: FTS5 + sqlite-vec (hybrid)
|
|
217
|
+
Phase 2: FTS5 + sqlite-vec + LLM reranking
|
|
218
|
+
Phase 3: FTS5 + sqlite-vec + reranking + importance scoring
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Dependencies to Consider
|
|
222
|
+
- `sentence-transformers` — Python embeddings (heavy, ~500MB)
|
|
223
|
+
- `sqlite-vec` — SQLite vector extension (lightweight)
|
|
224
|
+
- `hnswlib` — Approximate nearest neighbors (for large datasets)
|
|
225
|
+
- `flask`/`fastapi` — Web dashboard
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
*Last updated: 2026-07-16*
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: docs-haven
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Local knowledge base for AI agents with SQLite FTS5 search, URI routing, and conflict detection
|
|
5
5
|
Project-URL: Homepage, https://github.com/Cipher208/docs-haven
|
|
6
6
|
Project-URL: Documentation, https://github.com/Cipher208/docs-haven/tree/main/docs
|
|
@@ -34,7 +34,7 @@ Description-Content-Type: text/markdown
|
|
|
34
34
|
[](https://pypi.org/project/docs-haven/)
|
|
35
35
|
[](https://github.com/Cipher208/docs-haven/stargazers)
|
|
36
36
|
[](https://github.com/Cipher208/docs-haven/commits/main)
|
|
37
|
-
[](https://github.com/Cipher208/docs-haven/actions/workflows/ci.yml)
|
|
38
38
|
[](https://www.python.org/downloads/)
|
|
39
39
|
|
|
40
40
|
> **Your AI agent keeps forgetting what it learned last session. DocsHaven fixes that.**
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
[](https://pypi.org/project/docs-haven/)
|
|
5
5
|
[](https://github.com/Cipher208/docs-haven/stargazers)
|
|
6
6
|
[](https://github.com/Cipher208/docs-haven/commits/main)
|
|
7
|
-
[](https://github.com/Cipher208/docs-haven/actions/workflows/ci.yml)
|
|
8
8
|
[](https://www.python.org/downloads/)
|
|
9
9
|
|
|
10
10
|
> **Your AI agent keeps forgetting what it learned last session. DocsHaven fixes that.**
|
|
@@ -11,6 +11,14 @@ def get_storage() -> Storage:
|
|
|
11
11
|
return Storage(Path.home() / ".docshaven")
|
|
12
12
|
|
|
13
13
|
|
|
14
|
+
def check_error(result: dict, action: str = "Operation") -> bool:
|
|
15
|
+
"""Check if result contains an error. Returns True if error found."""
|
|
16
|
+
if "error" in result:
|
|
17
|
+
print(f"Error ({action}): {result['error']}")
|
|
18
|
+
return True
|
|
19
|
+
return False
|
|
20
|
+
|
|
21
|
+
|
|
14
22
|
def cmd_search(args):
|
|
15
23
|
"""Search the knowledge base."""
|
|
16
24
|
storage = get_storage()
|
|
@@ -28,8 +36,7 @@ def cmd_add(args):
|
|
|
28
36
|
"""Add a repository."""
|
|
29
37
|
storage = get_storage()
|
|
30
38
|
result = storage.add_repo(args.url, description=args.description)
|
|
31
|
-
if "
|
|
32
|
-
print(f"Error: {result['error']}")
|
|
39
|
+
if check_error(result, "Add repo"):
|
|
33
40
|
sys.exit(1)
|
|
34
41
|
print(f"Added {result['name']}: {result['files_indexed']} files, {result.get('chunks', 0)} chunks")
|
|
35
42
|
|
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
4
|
from pathlib import Path
|
|
5
|
-
from typing import
|
|
6
|
-
|
|
5
|
+
from typing import Any
|
|
7
6
|
|
|
8
7
|
DEFAULT_CONFIG_DIR = Path.home() / ".docshaven"
|
|
9
8
|
CONFIG_FILE = "config.json"
|
|
@@ -12,7 +11,7 @@ CONFIG_FILE = "config.json"
|
|
|
12
11
|
class Config:
|
|
13
12
|
"""DocsHaven configuration."""
|
|
14
13
|
|
|
15
|
-
def __init__(self, config_dir:
|
|
14
|
+
def __init__(self, config_dir: Path | None = None):
|
|
16
15
|
self.config_dir = config_dir or DEFAULT_CONFIG_DIR
|
|
17
16
|
self.config_path = self.config_dir / CONFIG_FILE
|
|
18
17
|
self._data = self._load()
|
|
@@ -29,12 +28,13 @@ class Config:
|
|
|
29
28
|
}
|
|
30
29
|
|
|
31
30
|
def save(self):
|
|
31
|
+
self.config_dir.mkdir(parents=True, exist_ok=True)
|
|
32
32
|
self.config_path.write_text(json.dumps(self._data, indent=2))
|
|
33
33
|
|
|
34
|
-
def get(self, key: str, default=None):
|
|
34
|
+
def get(self, key: str, default: Any = None) -> Any:
|
|
35
35
|
return self._data.get(key, default)
|
|
36
36
|
|
|
37
|
-
def set(self, key: str, value):
|
|
37
|
+
def set(self, key: str, value: Any) -> None:
|
|
38
38
|
self._data[key] = value
|
|
39
39
|
self.save()
|
|
40
40
|
|
|
@@ -6,7 +6,13 @@ If similar docs exist, flag potential conflicts for human review.
|
|
|
6
6
|
Uses Storage.search() instead of external QMD CLI.
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
9
11
|
from dataclasses import dataclass
|
|
12
|
+
from typing import TYPE_CHECKING
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from storage import Storage
|
|
10
16
|
|
|
11
17
|
|
|
12
18
|
@dataclass
|
|
@@ -30,7 +36,7 @@ class ConflictResult:
|
|
|
30
36
|
has_conflicts: bool
|
|
31
37
|
judgment_required: bool
|
|
32
38
|
|
|
33
|
-
def to_dict(self):
|
|
39
|
+
def to_dict(self) -> dict:
|
|
34
40
|
return {
|
|
35
41
|
"new_title": self.new_title,
|
|
36
42
|
"candidates": [vars(c) if hasattr(c, "__dataclass_fields__") else c for c in self.candidates],
|
|
@@ -45,10 +51,10 @@ class ConflictDetector:
|
|
|
45
51
|
SCORE_THRESHOLD = 0.3 # Minimum score to consider a candidate
|
|
46
52
|
MAX_CANDIDATES = 3
|
|
47
53
|
|
|
48
|
-
def __init__(self, storage=None):
|
|
54
|
+
def __init__(self, storage: Storage | None = None):
|
|
49
55
|
self._storage = storage
|
|
50
56
|
|
|
51
|
-
def _get_storage(self):
|
|
57
|
+
def _get_storage(self) -> Storage:
|
|
52
58
|
if self._storage is None:
|
|
53
59
|
from pathlib import Path
|
|
54
60
|
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""Result type for error handling — replaces error dict anti-pattern."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import Generic, TypeVar
|
|
7
|
+
|
|
8
|
+
T = TypeVar("T")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class Ok(Generic[T]):
|
|
13
|
+
"""Success result."""
|
|
14
|
+
|
|
15
|
+
value: T
|
|
16
|
+
|
|
17
|
+
def is_ok(self) -> bool:
|
|
18
|
+
return True
|
|
19
|
+
|
|
20
|
+
def is_err(self) -> bool:
|
|
21
|
+
return False
|
|
22
|
+
|
|
23
|
+
def unwrap(self) -> T:
|
|
24
|
+
return self.value
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass
|
|
28
|
+
class Err:
|
|
29
|
+
"""Error result."""
|
|
30
|
+
|
|
31
|
+
error: str
|
|
32
|
+
code: str = "error"
|
|
33
|
+
|
|
34
|
+
def is_ok(self) -> bool:
|
|
35
|
+
return False
|
|
36
|
+
|
|
37
|
+
def is_err(self) -> bool:
|
|
38
|
+
return True
|
|
39
|
+
|
|
40
|
+
def unwrap(self):
|
|
41
|
+
raise RuntimeError(self.error)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
Result = Ok | Err
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def ok(value: T) -> Ok[T]:
|
|
48
|
+
"""Create a success result."""
|
|
49
|
+
return Ok(value)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def err(error: str, code: str = "error") -> Err:
|
|
53
|
+
"""Create an error result."""
|
|
54
|
+
return Err(error=error, code=code)
|