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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bluera-knowledge",
3
- "version": "0.32.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.32.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