bluera-knowledge 0.32.0 → 0.33.0
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.
- package/.claude-plugin/plugin.json +1 -2
- package/CHANGELOG.md +22 -0
- package/hooks/hooks.json +10 -0
- package/hooks/posttooluse-websearch-bk.py +158 -0
- package/package.json +1 -2
- package/scripts/statusline-module.sh +29 -0
- package/skills/statusline/SKILL.md +19 -0
- /package/{commands/add-folder.md → skills/add-folder/SKILL.md} +0 -0
- /package/{commands/add-repo.md → skills/add-repo/SKILL.md} +0 -0
- /package/{commands/cancel.md → skills/cancel/SKILL.md} +0 -0
- /package/{commands/check-status.md → skills/check-status/SKILL.md} +0 -0
- /package/{commands/crawl.md → skills/crawl/SKILL.md} +0 -0
- /package/{commands/doctor.md → skills/doctor/SKILL.md} +0 -0
- /package/{commands/eval.md → skills/eval/SKILL.md} +0 -0
- /package/{commands/health.md → skills/health/SKILL.md} +0 -0
- /package/{commands/index.md → skills/index/SKILL.md} +0 -0
- /package/{commands/remove-store.md → skills/remove-store/SKILL.md} +0 -0
- /package/{commands/search.md → skills/search/SKILL.md} +0 -0
- /package/{commands → skills/search}/search.sh +0 -0
- /package/{commands/skill-activation.md → skills/skill-activation/SKILL.md} +0 -0
- /package/{commands/stores.md → skills/stores/SKILL.md} +0 -0
- /package/{commands/suggest.md → skills/suggest/SKILL.md} +0 -0
- /package/{commands/sync.md → skills/sync/SKILL.md} +0 -0
- /package/{commands/test-plugin.md → skills/test-plugin/SKILL.md} +0 -0
- /package/{commands/uninstall.md → skills/uninstall/SKILL.md} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bluera-knowledge",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.33.0",
|
|
4
4
|
"description": "Clone repos, crawl docs, search locally. Fast, authoritative answers for AI coding agents.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Bluera Inc",
|
|
@@ -18,7 +18,6 @@
|
|
|
18
18
|
"vector",
|
|
19
19
|
"embeddings"
|
|
20
20
|
],
|
|
21
|
-
"commands": "./commands/",
|
|
22
21
|
"skills": "./skills/",
|
|
23
22
|
"mcpServers": "./.mcp.json"
|
|
24
23
|
}
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
## [0.33.0](https://github.com/blueraai/bluera-knowledge/compare/v0.28.0...v0.33.0) (2026-03-01)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* add statusline module with store count display ([4446501](https://github.com/blueraai/bluera-knowledge/commit/44465015a4734cd7c253bda51c98ce149561fda8))
|
|
11
|
+
* **eval:** 3-agent comparison with BK Grep agent and token tracking ([b3045a8](https://github.com/blueraai/bluera-knowledge/commit/b3045a8ae7df7a2e13ecdf992ff4d1055521460b))
|
|
12
|
+
* **eval:** add agent quality eval comparing with-BK vs without-BK answers ([d8c62d8](https://github.com/blueraai/bluera-knowledge/commit/d8c62d804bd10d572049f4f2a01deb30cb00ccf5))
|
|
13
|
+
* **hooks:** add PostToolUse hook for WebSearch BK suggestions ([d0420b4](https://github.com/blueraai/bluera-knowledge/commit/d0420b473c73092b5e6a28be2699f5fc40e090c3))
|
|
14
|
+
* **mcp:** add file count estimation and ETA to store creation responses ([3d71a30](https://github.com/blueraai/bluera-knowledge/commit/3d71a307275fa06b9810ed606583f4ec2acc5841))
|
|
15
|
+
* **mcp:** add stores:pull command for git pull + re-index ([7ca809c](https://github.com/blueraai/bluera-knowledge/commit/7ca809c3675be19d9d0e29e71c5f75d4f40fce35))
|
|
16
|
+
* **mcp:** optimize search-to-read workflow with store paths, relatedFiles, find-files intent, and file-based get_full_context ([4eb9be9](https://github.com/blueraai/bluera-knowledge/commit/4eb9be97598e475576603b2b9a565946197986ac))
|
|
17
|
+
* search infrastructure, benchmark framework, and model registry ([285ff2f](https://github.com/blueraai/bluera-knowledge/commit/285ff2f5574c4d53b61b31e84fdec43553364e98))
|
|
18
|
+
* training pipeline, evaluation gate, and experiment docs ([d90b395](https://github.com/blueraai/bluera-knowledge/commit/d90b395a330f4ab09ac6d71db77d659c415aa87d))
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
### Bug Fixes
|
|
22
|
+
|
|
23
|
+
* **config:** wire BK_MODEL env var override in ConfigService.load() ([be56426](https://github.com/blueraai/bluera-knowledge/commit/be564263176410c3eb1441bf85c4fdce48ddc7c4))
|
|
24
|
+
* **mcp:** add runtime files to package.json files array and improve bootstrap error on corrupt cache ([e195f93](https://github.com/blueraai/bluera-knowledge/commit/e195f93a4600c65bcd0fcbb45380aaa1d32bb356))
|
|
25
|
+
* **mcp:** prevent browser postinstall from crashing bootstrap ([e27fe02](https://github.com/blueraai/bluera-knowledge/commit/e27fe021bdb34c8ca5b8bf93720a176435e7973c))
|
|
26
|
+
|
|
5
27
|
## [0.32.0](https://github.com/blueraai/bluera-knowledge/compare/v0.28.0...v0.32.0) (2026-02-28)
|
|
6
28
|
|
|
7
29
|
|
package/hooks/hooks.json
CHANGED
|
@@ -55,6 +55,16 @@
|
|
|
55
55
|
}
|
|
56
56
|
]
|
|
57
57
|
},
|
|
58
|
+
{
|
|
59
|
+
"matcher": "WebSearch",
|
|
60
|
+
"hooks": [
|
|
61
|
+
{
|
|
62
|
+
"type": "command",
|
|
63
|
+
"command": "python3 ${CLAUDE_PLUGIN_ROOT:-.}/hooks/posttooluse-websearch-bk.py",
|
|
64
|
+
"timeout": 2
|
|
65
|
+
}
|
|
66
|
+
]
|
|
67
|
+
},
|
|
58
68
|
{
|
|
59
69
|
"matcher": "mcp__.*bluera-knowledge__search",
|
|
60
70
|
"hooks": [
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
PostToolUse hook for bluera-knowledge plugin.
|
|
4
|
+
|
|
5
|
+
Fires after WebSearch tool calls. Checks if the search query relates to a
|
|
6
|
+
library that is already indexed in BK, and if so reminds Claude to use BK
|
|
7
|
+
search instead of relying solely on web results.
|
|
8
|
+
|
|
9
|
+
Deduplicates per-project so the same store only triggers once per hour.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import hashlib
|
|
13
|
+
import json
|
|
14
|
+
import os
|
|
15
|
+
import sys
|
|
16
|
+
import time
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# How long (seconds) before a seen store can trigger again
|
|
21
|
+
DEDUP_TTL = 3600 # 1 hour
|
|
22
|
+
|
|
23
|
+
# Minimum store name length to avoid false positives on short names
|
|
24
|
+
MIN_STORE_NAME_LEN = 3
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def get_stores_path() -> Path | None:
|
|
28
|
+
"""Get path to stores.json from project root."""
|
|
29
|
+
project_root = os.environ.get("PROJECT_ROOT") or os.environ.get("PWD")
|
|
30
|
+
if not project_root:
|
|
31
|
+
return None
|
|
32
|
+
return Path(project_root) / ".bluera" / "bluera-knowledge" / "data" / "stores.json"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def load_stores() -> list[dict]:
|
|
36
|
+
"""Load stores from stores.json. Returns empty list on error."""
|
|
37
|
+
stores_path = get_stores_path()
|
|
38
|
+
if stores_path is None or not stores_path.exists():
|
|
39
|
+
return []
|
|
40
|
+
try:
|
|
41
|
+
with open(stores_path, encoding="utf-8") as f:
|
|
42
|
+
data = json.load(f)
|
|
43
|
+
if isinstance(data, dict):
|
|
44
|
+
stores = data.get("stores", [])
|
|
45
|
+
return stores if isinstance(stores, list) else []
|
|
46
|
+
return data if isinstance(data, list) else []
|
|
47
|
+
except (json.JSONDecodeError, IOError, OSError):
|
|
48
|
+
return []
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _dedup_path(project_root: str) -> str:
|
|
52
|
+
"""Return the path to the per-project dedup file."""
|
|
53
|
+
h = hashlib.md5(project_root.encode()).hexdigest()[:8]
|
|
54
|
+
return f"/tmp/bk-websearch-{h}.json"
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def is_already_seen(store_name: str, project_root: str) -> bool:
|
|
58
|
+
"""Check if this store was already flagged within the TTL."""
|
|
59
|
+
try:
|
|
60
|
+
path = _dedup_path(project_root)
|
|
61
|
+
with open(path) as f:
|
|
62
|
+
seen: dict[str, float] = json.load(f)
|
|
63
|
+
ts = seen.get(store_name, 0)
|
|
64
|
+
return (time.time() - ts) < DEDUP_TTL
|
|
65
|
+
except Exception:
|
|
66
|
+
return False
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def mark_seen(store_name: str, project_root: str) -> None:
|
|
70
|
+
"""Record this store as seen, pruning expired entries."""
|
|
71
|
+
try:
|
|
72
|
+
path = _dedup_path(project_root)
|
|
73
|
+
now = time.time()
|
|
74
|
+
seen: dict[str, float] = {}
|
|
75
|
+
try:
|
|
76
|
+
with open(path) as f:
|
|
77
|
+
seen = json.load(f)
|
|
78
|
+
except Exception:
|
|
79
|
+
pass
|
|
80
|
+
seen = {k: v for k, v in seen.items() if (now - v) < DEDUP_TTL}
|
|
81
|
+
seen[store_name] = now
|
|
82
|
+
with open(path, "w") as f:
|
|
83
|
+
json.dump(seen, f)
|
|
84
|
+
except Exception:
|
|
85
|
+
pass
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def find_matching_stores(query: str, stores: list[dict]) -> list[str]:
|
|
89
|
+
"""Find store names that appear in the search query."""
|
|
90
|
+
query_lower = query.lower()
|
|
91
|
+
matched = []
|
|
92
|
+
for store in stores:
|
|
93
|
+
name = store.get("name", "")
|
|
94
|
+
if len(name) < MIN_STORE_NAME_LEN:
|
|
95
|
+
continue
|
|
96
|
+
if name.lower() in query_lower:
|
|
97
|
+
matched.append(name)
|
|
98
|
+
return matched
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def main() -> int:
|
|
102
|
+
try:
|
|
103
|
+
stdin_data = sys.stdin.read()
|
|
104
|
+
if not stdin_data.strip():
|
|
105
|
+
return 0
|
|
106
|
+
hook_input = json.loads(stdin_data)
|
|
107
|
+
|
|
108
|
+
tool_name = hook_input.get("tool_name", "")
|
|
109
|
+
if tool_name != "WebSearch":
|
|
110
|
+
return 0
|
|
111
|
+
|
|
112
|
+
tool_input = hook_input.get("tool_input", {})
|
|
113
|
+
query = tool_input.get("query", "")
|
|
114
|
+
if not query:
|
|
115
|
+
return 0
|
|
116
|
+
|
|
117
|
+
stores = load_stores()
|
|
118
|
+
if not stores:
|
|
119
|
+
return 0
|
|
120
|
+
|
|
121
|
+
matched = find_matching_stores(query, stores)
|
|
122
|
+
if not matched:
|
|
123
|
+
return 0
|
|
124
|
+
|
|
125
|
+
project_root = os.environ.get("PROJECT_ROOT", os.environ.get("PWD", ""))
|
|
126
|
+
|
|
127
|
+
# Filter out already-seen stores
|
|
128
|
+
unseen = [s for s in matched if not is_already_seen(s, project_root)]
|
|
129
|
+
if not unseen:
|
|
130
|
+
return 0
|
|
131
|
+
|
|
132
|
+
for store_name in unseen:
|
|
133
|
+
mark_seen(store_name, project_root)
|
|
134
|
+
|
|
135
|
+
store_list = ", ".join(unseen)
|
|
136
|
+
message = (
|
|
137
|
+
f"You searched the web for a topic related to indexed libraries: {store_list}\n"
|
|
138
|
+
f"You have authoritative source code indexed in Bluera Knowledge.\n"
|
|
139
|
+
f"Use mcp__bluera-knowledge__search to get definitive answers from the actual source:\n"
|
|
140
|
+
f" mcp__bluera-knowledge__search(query='{query}', intent='find-implementation')\n"
|
|
141
|
+
f"This is faster and more accurate than web results."
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
output = {
|
|
145
|
+
"hookSpecificOutput": {
|
|
146
|
+
"hookEventName": "PostToolUse",
|
|
147
|
+
"additionalContext": message,
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
print(json.dumps(output))
|
|
151
|
+
return 0
|
|
152
|
+
|
|
153
|
+
except Exception:
|
|
154
|
+
return 0
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
if __name__ == "__main__":
|
|
158
|
+
raise SystemExit(main())
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bluera-knowledge",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.33.0",
|
|
4
4
|
"description": "CLI tool for managing knowledge stores with semantic search",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -76,7 +76,6 @@
|
|
|
76
76
|
"files": [
|
|
77
77
|
"dist/",
|
|
78
78
|
"python/",
|
|
79
|
-
"commands/",
|
|
80
79
|
"skills/",
|
|
81
80
|
"hooks/",
|
|
82
81
|
"scripts/",
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# bluera-knowledge statusline module
|
|
3
|
+
# Injected into ~/.claude/statusline.sh between boundary comments:
|
|
4
|
+
# # --- bluera-knowledge ---
|
|
5
|
+
# # --- end bluera-knowledge ---
|
|
6
|
+
#
|
|
7
|
+
# Expected variables from parent script:
|
|
8
|
+
# PROJECT_DIR - project directory path (from statusline JSON input)
|
|
9
|
+
#
|
|
10
|
+
# Exports:
|
|
11
|
+
# BK_STATUS - formatted status string (e.g. " 📘●3")
|
|
12
|
+
|
|
13
|
+
get_bk_status() {
|
|
14
|
+
local project_dir="$1"
|
|
15
|
+
local store_count=""
|
|
16
|
+
local config_file="$project_dir/.bluera/bluera-knowledge/stores.config.json"
|
|
17
|
+
if [ -f "$config_file" ]; then
|
|
18
|
+
store_count=$(jq '.stores | length' "$config_file" 2>/dev/null)
|
|
19
|
+
[ "$store_count" = "0" ] && store_count=""
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
if pgrep -f "bluera-knowledge.*bootstrap" >/dev/null 2>&1 || \
|
|
23
|
+
pgrep -f "bluera-knowledge-mcp" >/dev/null 2>&1; then
|
|
24
|
+
printf " 📘\033[32m●\033[0m%s" "$store_count"
|
|
25
|
+
else
|
|
26
|
+
printf " 📘\033[31m●\033[0m%s" "$store_count"
|
|
27
|
+
fi
|
|
28
|
+
}
|
|
29
|
+
BK_STATUS=$(get_bk_status "$PROJECT_DIR")
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: statusline
|
|
3
|
+
description: Install or update the bluera-knowledge statusline module
|
|
4
|
+
allowed-tools: [Read, Edit, Write, Bash]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Install the bluera-knowledge statusline module into the user's Claude Code statusline.
|
|
8
|
+
|
|
9
|
+
## Steps
|
|
10
|
+
|
|
11
|
+
1. Locate the plugin root by finding `scripts/statusline-module.sh` relative to this command file
|
|
12
|
+
2. Read `scripts/statusline-module.sh` from the plugin directory to get the latest module content
|
|
13
|
+
3. Read `~/.claude/statusline.sh` (or `$CLAUDE_CONFIG_DIR/statusline.sh`)
|
|
14
|
+
4. If the file doesn't exist, tell the user to first set up a statusline with `/bluera-base:claude-code-statusline`
|
|
15
|
+
5. Look for existing boundary comments `# --- bluera-knowledge ---` / `# --- end bluera-knowledge ---`
|
|
16
|
+
6. If found: replace everything between the boundary comments (exclusive) with the module content (the function and call site from `statusline-module.sh`, excluding the shebang and header comments)
|
|
17
|
+
7. If not found: insert the full boundary-commented section before the final output `printf` statements, and ensure `$BK_STATUS` is referenced in the `PLUGIN_SEG` variable
|
|
18
|
+
8. Ensure the call site passes `"$PROJECT_DIR"` to `get_bk_status`
|
|
19
|
+
9. Show the user the updated section and confirm success
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|