bluera-knowledge 0.16.2 → 0.16.4

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,17 +1,9 @@
1
1
  {
2
2
  "name": "bluera-knowledge",
3
- "version": "0.16.2",
3
+ "version": "0.16.4",
4
4
  "description": "Clone repos, crawl docs, search locally. Fast, authoritative answers for AI coding agents.",
5
5
  "author": {
6
6
  "name": "Bluera Inc",
7
7
  "url": "https://bluera.ai"
8
- },
9
- "prebuiltBinaries": {
10
- "manifestUrl": "https://github.com/blueraai/bluera-knowledge/releases/latest/download/manifest.json",
11
- "platforms": [
12
- "darwin-arm64",
13
- "darwin-x64",
14
- "linux-x64"
15
- ]
16
8
  }
17
9
  }
package/CHANGELOG.md CHANGED
@@ -2,6 +2,26 @@
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.16.4](https://github.com/blueraai/bluera-knowledge/compare/v0.16.3...v0.16.4) (2026-01-17)
6
+
7
+
8
+ ### Features
9
+
10
+ * **test:** expand test-plugin to cover full API surface ([e473f09](https://github.com/blueraai/bluera-knowledge/commit/e473f09003f311ca8648d60ccf56fbd7f99bd480))
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * **bootstrap:** use system tar for extraction ([3a17b33](https://github.com/blueraai/bluera-knowledge/commit/3a17b33b99aa6ae0d35b2f2a4a96daa444336e99))
16
+
17
+ ## [0.16.3](https://github.com/blueraai/bluera-knowledge/compare/v0.16.1...v0.16.3) (2026-01-17)
18
+
19
+
20
+ ### Bug Fixes
21
+
22
+ * **plugin:** add author field to manifest ([9bf15c2](https://github.com/blueraai/bluera-knowledge/commit/9bf15c243b735d4a1763de9b3601781c6e4aeaf6))
23
+ * **plugin:** remove unrecognized prebuiltBinaries field ([329ac08](https://github.com/blueraai/bluera-knowledge/commit/329ac08b4f794e016bbfedc0d24c63491e9a7dfb))
24
+
5
25
  ## [0.16.2](https://github.com/blueraai/bluera-knowledge/compare/v0.16.1...v0.16.2) (2026-01-17)
6
26
 
7
27
 
@@ -6,22 +6,29 @@ context: fork
6
6
 
7
7
  # Test Plugin
8
8
 
9
- Comprehensive test of all Bluera Knowledge plugin functionality.
9
+ Comprehensive test of all Bluera Knowledge plugin functionality covering the **full API surface**.
10
+
11
+ ## API Coverage
12
+
13
+ | Category | Tests |
14
+ |----------|-------|
15
+ | MCP execute commands | help, commands, stores, store:create, store:info, store:index, store:delete, jobs, job:status, job:cancel |
16
+ | MCP tools | search, get_full_context |
17
+ | Slash commands | stores, search, suggest, check-status, skill-activation, index, add-folder |
18
+ | Web crawling | crawl (start job, verify status, cancel) |
19
+ | Hooks | PostToolUse, UserPromptSubmit, SessionStart |
10
20
 
11
21
  ## When to Use Each Mode
12
22
 
13
23
  | Scenario | Command | Tests |
14
24
  |----------|---------|-------|
15
- | Claude running with `--plugin-dir .` | `/test-plugin` | 19/19 (full suite) |
16
- | Plugin installed from marketplace | `/test-plugin` | 19/19 (full suite) |
17
- | Claude running WITHOUT plugin loaded | `/test-plugin --dev` | 16/19 (no slash cmds) |
18
- | CI/CD or scripted testing (no Claude) | `--dev` via bash | 16/19 (no slash cmds) |
25
+ | Claude running with `--plugin-dir .` | `/test-plugin` | 30/30 (full suite) |
26
+ | Plugin installed from marketplace | `/test-plugin` | 30/30 (full suite) |
27
+ | Claude running WITHOUT plugin loaded | `/test-plugin --dev` | 24/30 (no slash cmds) |
19
28
 
20
29
  **Modes:**
21
- - **Default mode**: Uses Claude's MCP tools. Works when plugin is loaded via `--plugin-dir .` or installed from marketplace. Runs all 19 tests including slash commands.
22
- - **`--dev` mode**: Spawns MCP server directly via `scripts/test-mcp-dev.js`. Works without Claude Code or when plugin isn't loaded. Skips slash command tests (8-10) since they require Claude's skill router.
23
-
24
- **Recommendation:** If developing with `claude --plugin-dir .`, use default mode (no `--dev` flag) for full test coverage.
30
+ - **Default mode**: Uses Claude's MCP tools. Runs all 30 tests including slash commands.
31
+ - **`--dev` mode**: Spawns MCP server directly. Skips slash command tests since they require Claude's skill router.
25
32
 
26
33
  ## Context
27
34
 
@@ -31,24 +38,26 @@ Comprehensive test of all Bluera Knowledge plugin functionality.
31
38
 
32
39
  Check if `--dev` flag is present in: $ARGUMENTS
33
40
 
34
- - If `--dev` is present: Use **Development Mode** (MCP via scripts/test-mcp-dev.js)
35
- - Otherwise: Use **Production Mode** (MCP via Claude's built-in tools)
41
+ - If `--dev` is present: Use **Development Mode**
42
+ - Otherwise: Use **Production Mode**
36
43
 
37
44
  ---
38
45
 
39
46
  ## Pre-Test Cleanup
40
47
 
41
- First, clean up any leftover artifacts from previous test runs (ignore errors if they don't exist):
48
+ Clean up any leftover artifacts from previous test runs (ignore errors):
42
49
 
43
50
  **In `--dev` mode:**
44
51
  ```bash
45
52
  node scripts/test-mcp-dev.js call execute '{"command":"store:delete","args":{"store":"bk-test-store"}}' 2>/dev/null || true
53
+ node scripts/test-mcp-dev.js call execute '{"command":"store:delete","args":{"store":"bk-crawl-test"}}' 2>/dev/null || true
46
54
  rm -rf .bluera/bluera-knowledge/test-content
47
55
  ```
48
56
 
49
57
  **In production mode:**
50
- 1. Call MCP tool `execute` with `{ command: "store:delete", args: { store: "bk-test-store" } }` - ignore errors
51
- 2. Run: `rm -rf .bluera/bluera-knowledge/test-content`
58
+ 1. Call MCP `execute` with `{ command: "store:delete", args: { store: "bk-test-store" } }` - ignore errors
59
+ 2. Call MCP `execute` with `{ command: "store:delete", args: { store: "bk-crawl-test" } }` - ignore errors
60
+ 3. Run: `rm -rf .bluera/bluera-knowledge/test-content`
52
61
 
53
62
  ## Test Content Setup
54
63
 
@@ -70,253 +79,318 @@ Keywords: bluera-knowledge-test, plugin-validation, mcp-test
70
79
  EOF
71
80
  ```
72
81
 
73
- ## Workflow
82
+ ---
74
83
 
75
- Execute each test in order. Mark each as PASS or FAIL.
84
+ # PRODUCTION MODE (default)
76
85
 
77
- ---
86
+ Use this section if `--dev` flag is NOT present.
78
87
 
79
- # DEVELOPMENT MODE (`--dev`)
88
+ ## Part 1: MCP Execute Commands
80
89
 
81
- Use this section if `--dev` flag is present. Uses `scripts/test-mcp-dev.js` to spawn MCP server directly.
90
+ 1. **help**: Call `execute` with `{ command: "help" }`
91
+ - PASS if response contains "Available commands"
82
92
 
83
- ### Part 1: MCP Server Tests (via dev script)
93
+ 2. **commands**: Call `execute` with `{ command: "commands" }`
94
+ - PASS if response contains array of commands with "stores" and "store:create"
84
95
 
85
- 1. **MCP Connection**:
86
- ```bash
87
- node scripts/test-mcp-dev.js call execute '{"command":"help"}'
88
- ```
89
- - PASS if output contains "Available commands"
96
+ 3. **stores**: Call `execute` with `{ command: "stores" }`
97
+ - PASS if no error (may return empty array)
90
98
 
91
- 2. **List Stores**:
92
- ```bash
93
- node scripts/test-mcp-dev.js call execute '{"command":"stores"}'
99
+ 4. **store:create**: Call `execute` with:
100
+ ```json
101
+ { "command": "store:create", "args": { "name": "bk-test-store", "type": "file", "source": ".bluera/bluera-knowledge/test-content" } }
94
102
  ```
95
- - PASS if no error (may return empty array)
103
+ - PASS if response indicates success and contains store id
96
104
 
97
- 3. **Create Store**:
98
- ```bash
99
- node scripts/test-mcp-dev.js call execute '{"command":"store:create","args":{"name":"bk-test-store","type":"file","source":".bluera/bluera-knowledge/test-content"}}'
105
+ 5. **store:info**: Call `execute` with:
106
+ ```json
107
+ { "command": "store:info", "args": { "store": "bk-test-store" } }
100
108
  ```
101
- - PASS if response indicates success
109
+ - PASS if response contains store name and type
102
110
 
103
- 4. **Store Info**:
104
- ```bash
105
- node scripts/test-mcp-dev.js call execute '{"command":"store:info","args":{"store":"bk-test-store"}}'
111
+ 6. **store:index**: Call `execute` with:
112
+ ```json
113
+ { "command": "store:index", "args": { "store": "bk-test-store" } }
106
114
  ```
107
- - PASS if response contains store details
115
+ - PASS if response indicates indexing started (job created)
108
116
 
109
- 5. **Index Store** (via CLI for reliability):
110
- ```bash
111
- node dist/index.js index bk-test-store
117
+ 7. **jobs**: Call `execute` with `{ command: "jobs" }`
118
+ - PASS if response contains jobs array (may be empty if indexing completed)
119
+
120
+ 8. **job:status** (if job from step 6 available): Call `execute` with:
121
+ ```json
122
+ { "command": "job:status", "args": { "jobId": "<job-id-from-step-6>" } }
112
123
  ```
113
- - PASS if output shows "Indexed X documents"
124
+ - PASS if response contains job status (pending, running, or completed)
125
+ - SKIP if no job id available (indexing was synchronous)
114
126
 
115
- 6-7. **Search + Get Full Context** (session mode to maintain cache):
116
- ```bash
117
- node scripts/test-mcp-dev.js session << 'EOF'
118
- search {"query":"validateBKPlugin","stores":["bk-test-store"]}
119
- get_full_context {"resultId":"$LAST_ID"}
120
- EOF
127
+ ## Part 2: MCP Search Tools
128
+
129
+ 9. **search**: Call MCP tool `search` with:
130
+ ```json
131
+ { "query": "validateBKPlugin", "stores": ["bk-test-store"] }
121
132
  ```
122
- - PASS if first result has non-empty results array
123
- - PASS if second result contains "BK Plugin Test File"
133
+ - PASS if results array is non-empty
134
+
135
+ 10. **get_full_context**: Call MCP tool `get_full_context` with resultId from step 9
136
+ - PASS if response contains code or documentation content
137
+
138
+ ## Part 3: Slash Commands
139
+
140
+ 11. **/stores**: Run `/bluera-knowledge:stores`
141
+ - PASS if bk-test-store is listed
142
+
143
+ 12. **/search**: Run `/bluera-knowledge:search "bluera-knowledge-test"`
144
+ - PASS if results are shown
124
145
 
125
- ### Part 2: Slash Commands (SKIPPED in --dev mode)
146
+ 13. **/suggest**: Run `/bluera-knowledge:suggest`
147
+ - PASS if no error thrown
148
+
149
+ 14. **/check-status**: Run `/bluera-knowledge:check-status`
150
+ - PASS if shows job status (may show "no active jobs")
151
+
152
+ 15. **/skill-activation**: Run `/bluera-knowledge:skill-activation`
153
+ - PASS if shows activation configuration or prompts for setup
154
+
155
+ 16. **/index**: Run `/bluera-knowledge:index bk-test-store`
156
+ - PASS if indexing completes or starts successfully
157
+
158
+ ## Part 4: Web Crawl Testing
159
+
160
+ 17. **Create crawl store**: Call `execute` with:
161
+ ```json
162
+ { "command": "store:create", "args": { "name": "bk-crawl-test", "type": "web", "source": "https://httpbin.org/html", "options": { "maxPages": 1 } } }
163
+ ```
164
+ - PASS if store creation initiates and returns job id
165
+
166
+ 18. **Verify crawl job**: Call `execute` with `{ command: "jobs" }`
167
+ - PASS if bk-crawl-test job appears in list (or completed)
168
+
169
+ 19. **job:cancel** (if crawl still running): Call `execute` with:
170
+ ```json
171
+ { "command": "job:cancel", "args": { "jobId": "<crawl-job-id>" } }
172
+ ```
173
+ - PASS if job cancelled or already completed
174
+ - SKIP if job already completed
175
+
176
+ 20. **Delete crawl store**: Call `execute` with:
177
+ ```json
178
+ { "command": "store:delete", "args": { "store": "bk-crawl-test" } }
179
+ ```
180
+ - PASS if deletion succeeds
181
+
182
+ ## Part 5: Hook Tests
183
+
184
+ 21. **Hook Registration**:
185
+ ```bash
186
+ cat hooks/hooks.json | jq -e '.hooks.PostToolUse and .hooks.UserPromptSubmit and .hooks.SessionStart'
187
+ ```
188
+ - PASS if returns `true`
126
189
 
127
- Tests 8-10 are skipped in development mode because slash commands require Claude Code to invoke them. The MCP server tests (Part 1) provide equivalent coverage of the underlying functionality.
190
+ 22. **PostToolUse Hook - Library Detection**:
191
+ ```bash
192
+ echo '{"tool_name": "Read", "tool_input": {"file_path": "/project/node_modules/express/index.js"}}' | python3 hooks/posttooluse-bk-reminder.py
193
+ ```
194
+ - PASS if output contains "BLUERA-KNOWLEDGE REMINDER"
128
195
 
129
- ### Part 3: Hook Tests (same for both modes)
196
+ 23. **PostToolUse Hook - Non-Library**:
197
+ ```bash
198
+ echo '{"tool_name": "Read", "tool_input": {"file_path": "/project/src/index.ts"}}' | python3 hooks/posttooluse-bk-reminder.py
199
+ ```
200
+ - PASS if output is empty
130
201
 
131
- [Continue to Part 3 below]
202
+ 24. **Skill Activation Hook - Matching**:
203
+ ```bash
204
+ export CLAUDE_PLUGIN_ROOT="$(pwd)" && echo '{"prompt": "why does the express package throw this error?"}' | python3 hooks/skill-activation.py
205
+ ```
206
+ - PASS if output contains "MANDATORY EVALUATION"
132
207
 
133
- ### Part 4: Cleanup (--dev mode)
208
+ 25. **Skill Activation Hook - Excluded**:
209
+ ```bash
210
+ export CLAUDE_PLUGIN_ROOT="$(pwd)" && echo '{"prompt": "/bluera-knowledge:search express"}' | python3 hooks/skill-activation.py
211
+ ```
212
+ - PASS if output is empty
134
213
 
135
- 17. **Delete Store**:
214
+ 26. **Skill Rules File**:
136
215
  ```bash
137
- node scripts/test-mcp-dev.js call execute '{"command":"store:delete","args":{"store":"bk-test-store"}}'
216
+ jq -e '(.skills | length) > 0 and (.globalExclusions | length) > 0' hooks/skill-rules.json
217
+ ```
218
+ - PASS if returns `true`
219
+
220
+ ## Part 6: Cleanup
221
+
222
+ 27. **Delete test store**: Call `execute` with:
223
+ ```json
224
+ { "command": "store:delete", "args": { "store": "bk-test-store" } }
138
225
  ```
139
226
  - PASS if deletion succeeds
140
227
 
141
- 18. **Remove Test Content**:
228
+ 28. **Remove test content**:
142
229
  ```bash
143
230
  rm -rf .bluera/bluera-knowledge/test-content
144
231
  ```
145
232
  - PASS if command succeeds
146
233
 
147
- 19. **Verify Cleanup**:
234
+ 29. **Verify store cleanup**: Call `execute` with `{ command: "stores" }`
235
+ - PASS if bk-test-store is NOT in the list
236
+
237
+ 30. **Verify no test artifacts**:
148
238
  ```bash
149
- node scripts/test-mcp-dev.js call execute '{"command":"stores"}'
239
+ test ! -d .bluera/bluera-knowledge/test-content && echo "clean"
150
240
  ```
151
- - PASS if bk-test-store is NOT in the list
241
+ - PASS if outputs "clean"
152
242
 
153
243
  ---
154
244
 
155
- # PRODUCTION MODE (default)
156
-
157
- Use this section if `--dev` flag is NOT present. Uses Claude's built-in MCP tools.
158
-
159
- ### Part 1: MCP Tools
245
+ # DEVELOPMENT MODE (`--dev`)
160
246
 
161
- 1. **MCP Connection**: Call MCP tool `execute` with `{ command: "help" }`
162
- - PASS if response contains "Available commands"
247
+ Use this section if `--dev` flag is present. Uses `scripts/test-mcp-dev.js`.
163
248
 
164
- 2. **List Stores (MCP)**: Call MCP tool `execute` with `{ command: "stores" }`
165
- - PASS if no error
249
+ ### Part 1: MCP Commands (via dev script)
166
250
 
167
- 3. **Create Store**: Call MCP tool `execute` with:
168
- ```json
169
- { "command": "store:create", "args": { "name": "bk-test-store", "type": "file", "source": ".bluera/bluera-knowledge/test-content" } }
251
+ 1. **help**:
252
+ ```bash
253
+ node scripts/test-mcp-dev.js call execute '{"command":"help"}'
170
254
  ```
171
- - PASS if response indicates success
255
+ - PASS if contains "Available commands"
172
256
 
173
- 4. **Store Info**: Call MCP tool `execute` with:
174
- ```json
175
- { "command": "store:info", "args": { "store": "bk-test-store" } }
257
+ 2. **commands**:
258
+ ```bash
259
+ node scripts/test-mcp-dev.js call execute '{"command":"commands"}'
176
260
  ```
177
- - PASS if response contains store details
261
+ - PASS if contains commands array
178
262
 
179
- 5. **Index Store**: Run CLI command via Bash:
263
+ 3. **stores**:
180
264
  ```bash
181
- node dist/index.js index bk-test-store
265
+ node scripts/test-mcp-dev.js call execute '{"command":"stores"}'
182
266
  ```
183
- - PASS if indexing completes successfully
267
+ - PASS if no error
184
268
 
185
- 6. **Search (MCP)**: Call MCP tool `search` with:
186
- ```json
187
- { "query": "validateBKPlugin", "stores": ["bk-test-store"] }
269
+ 4. **store:create**:
270
+ ```bash
271
+ node scripts/test-mcp-dev.js call execute '{"command":"store:create","args":{"name":"bk-test-store","type":"file","source":".bluera/bluera-knowledge/test-content"}}'
188
272
  ```
189
- - PASS if results array is non-empty
273
+ - PASS if success
190
274
 
191
- 7. **Get Full Context**: Call MCP tool `get_full_context` with resultId from step 6
192
- - PASS if response contains "BK Plugin Test File"
275
+ 5. **store:info**:
276
+ ```bash
277
+ node scripts/test-mcp-dev.js call execute '{"command":"store:info","args":{"store":"bk-test-store"}}'
278
+ ```
279
+ - PASS if contains store details
193
280
 
194
- ### Part 2: Slash Commands
281
+ 6. **store:index**:
282
+ ```bash
283
+ node scripts/test-mcp-dev.js call execute '{"command":"store:index","args":{"store":"bk-test-store"}}'
284
+ ```
285
+ - PASS if indexing starts
195
286
 
196
- 8. **Stores Command**: Run `/bluera-knowledge:stores`
197
- - PASS if bk-test-store is listed
287
+ 7. **jobs**:
288
+ ```bash
289
+ node scripts/test-mcp-dev.js call execute '{"command":"jobs"}'
290
+ ```
291
+ - PASS if returns jobs array
198
292
 
199
- 9. **Search Command**: Run `/bluera-knowledge:search "bluera-knowledge-test"`
200
- - PASS if results are shown
293
+ 8. **job:status** (if job available):
294
+ ```bash
295
+ node scripts/test-mcp-dev.js call execute '{"command":"job:status","args":{"jobId":"<job-id>"}}'
296
+ ```
297
+ - PASS if returns status
201
298
 
202
- 10. **Suggest Command**: Run `/bluera-knowledge:suggest`
203
- - PASS if no error thrown
299
+ ### Part 2: Search (via dev script)
204
300
 
205
- ### Part 3: Hook Tests (same for both modes)
301
+ 9-10. **Search + Get Full Context**:
302
+ ```bash
303
+ node scripts/test-mcp-dev.js session << 'EOF'
304
+ search {"query":"validateBKPlugin","stores":["bk-test-store"]}
305
+ get_full_context {"resultId":"$LAST_ID"}
306
+ EOF
307
+ ```
308
+ - PASS if search returns results and context contains content
206
309
 
207
- [Continue to Part 3 below]
310
+ ### Part 3: Slash Commands (SKIPPED in --dev mode)
208
311
 
209
- ### Part 4: Cleanup (production mode)
312
+ Tests 11-16 are skipped - slash commands require Claude's skill router.
210
313
 
211
- 17. **Delete Store**: Call MCP tool `execute` with:
212
- ```json
213
- { "command": "store:delete", "args": { "store": "bk-test-store" } }
214
- ```
215
- - PASS if deletion succeeds
314
+ ### Part 4: Web Crawl (via dev script)
216
315
 
217
- 18. **Remove Test Content**:
316
+ 17. **Create crawl store**:
218
317
  ```bash
219
- rm -rf .bluera/bluera-knowledge/test-content
318
+ node scripts/test-mcp-dev.js call execute '{"command":"store:create","args":{"name":"bk-crawl-test","type":"web","source":"https://httpbin.org/html","options":{"maxPages":1}}}'
220
319
  ```
221
- - PASS if command succeeds
222
-
223
- 19. **Verify Cleanup**: Call MCP tool `execute` with `{ command: "stores" }`
224
- - PASS if bk-test-store is NOT in the list
225
-
226
- ---
227
-
228
- # Part 3: Hook Tests (both modes)
229
-
230
- These tests verify that plugin hooks work correctly by running hook scripts directly.
320
+ - PASS if returns job id
231
321
 
232
- 11. **Hook Registration**:
322
+ 18. **Verify crawl job**:
233
323
  ```bash
234
- cat hooks/hooks.json | jq -e '.hooks.PostToolUse and .hooks.UserPromptSubmit and .hooks.SessionStart'
324
+ node scripts/test-mcp-dev.js call execute '{"command":"jobs"}'
235
325
  ```
236
- - PASS if returns `true`
326
+ - PASS if job appears
237
327
 
238
- 12. **PostToolUse Hook - Library Detection**:
328
+ 19. **job:cancel** (if running):
239
329
  ```bash
240
- echo '{"tool_name": "Read", "tool_input": {"file_path": "/project/node_modules/express/index.js"}}' | python3 hooks/posttooluse-bk-reminder.py
330
+ node scripts/test-mcp-dev.js call execute '{"command":"job:cancel","args":{"jobId":"<crawl-job-id>"}}'
241
331
  ```
242
- - PASS if output contains "BLUERA-KNOWLEDGE REMINDER" and "express"
332
+ - PASS if cancelled or completed
243
333
 
244
- 13. **PostToolUse Hook - Non-Library (Silent)**:
334
+ 20. **Delete crawl store**:
245
335
  ```bash
246
- echo '{"tool_name": "Read", "tool_input": {"file_path": "/project/src/index.ts"}}' | python3 hooks/posttooluse-bk-reminder.py
336
+ node scripts/test-mcp-dev.js call execute '{"command":"store:delete","args":{"store":"bk-crawl-test"}}'
247
337
  ```
248
- - PASS if output is empty
338
+ - PASS if deleted
249
339
 
250
- 14. **Skill Activation Hook - Matching Prompt**:
251
- ```bash
252
- export CLAUDE_PLUGIN_ROOT="$(pwd)" && echo '{"prompt": "why does the express package throw this error?"}' | python3 hooks/skill-activation.py
253
- ```
254
- - PASS if output contains "MANDATORY EVALUATION"
340
+ ### Part 5: Hook Tests (same as production)
255
341
 
256
- 15. **Skill Activation Hook - Excluded Prompt**:
257
- ```bash
258
- export CLAUDE_PLUGIN_ROOT="$(pwd)" && echo '{"prompt": "/bluera-knowledge:search express"}' | python3 hooks/skill-activation.py
259
- ```
260
- - PASS if output is empty
342
+ [Tests 21-26 - same bash commands as production mode]
261
343
 
262
- 16. **Skill Rules File**:
263
- ```bash
264
- jq -e '(.skills | length) > 0 and (.globalExclusions | length) > 0' hooks/skill-rules.json
265
- ```
266
- - PASS if returns `true`
344
+ ### Part 6: Cleanup (via dev script)
345
+
346
+ 27-30: Same cleanup steps using dev script for MCP calls.
267
347
 
268
348
  ---
269
349
 
270
350
  ## Output Format
271
351
 
272
- ### For `--dev` mode (16 tests):
352
+ ### Production mode (30 tests):
273
353
 
274
354
  | # | Test | Status |
275
355
  |---|------|--------|
276
- | 1 | MCP Connection (help) | ? |
277
- | 2 | List Stores | ? |
278
- | 3 | Create Store | ? |
279
- | 4 | Store Info | ? |
280
- | 5 | Index Store | ? |
281
- | 6-7 | Search + Get Full Context | ? |
282
- | 8-10 | Slash Commands | SKIPPED (--dev mode) |
283
- | 11 | Hook Registration | ? |
284
- | 12 | PostToolUse - Library Detection | ? |
285
- | 13 | PostToolUse - Non-Library | ? |
286
- | 14 | Skill Activation - Matching | ? |
287
- | 15 | Skill Activation - Excluded | ? |
288
- | 16 | Skill Rules File | ? |
289
- | 17 | Delete Store | ? |
290
- | 18 | Remove Test Content | ? |
291
- | 19 | Verify Cleanup | ? |
292
-
293
- **Result: X/16 tests passed (3 skipped)**
294
-
295
- ### For production mode (19 tests):
296
-
297
- | # | Test | Status |
298
- |---|------|--------|
299
- | 1 | MCP Connection (help) | ? |
300
- | 2 | List Stores (MCP) | ? |
301
- | 3 | Create Store | ? |
302
- | 4 | Store Info | ? |
303
- | 5 | Index Store | ? |
304
- | 6 | Search (MCP) | ? |
305
- | 7 | Get Full Context | ? |
306
- | 8 | /stores Command | ? |
307
- | 9 | /search Command | ? |
308
- | 10 | /suggest Command | ? |
309
- | 11 | Hook Registration | ? |
310
- | 12 | PostToolUse - Library Detection | ? |
311
- | 13 | PostToolUse - Non-Library | ? |
312
- | 14 | Skill Activation - Matching | ? |
313
- | 15 | Skill Activation - Excluded | ? |
314
- | 16 | Skill Rules File | ? |
315
- | 17 | Delete Store | ? |
316
- | 18 | Remove Test Content | ? |
317
- | 19 | Verify Cleanup | ? |
318
-
319
- **Result: X/19 tests passed**
356
+ | 1 | MCP help | ? |
357
+ | 2 | MCP commands | ? |
358
+ | 3 | MCP stores | ? |
359
+ | 4 | MCP store:create | ? |
360
+ | 5 | MCP store:info | ? |
361
+ | 6 | MCP store:index | ? |
362
+ | 7 | MCP jobs | ? |
363
+ | 8 | MCP job:status | ? |
364
+ | 9 | MCP search | ? |
365
+ | 10 | MCP get_full_context | ? |
366
+ | 11 | /stores | ? |
367
+ | 12 | /search | ? |
368
+ | 13 | /suggest | ? |
369
+ | 14 | /check-status | ? |
370
+ | 15 | /skill-activation | ? |
371
+ | 16 | /index | ? |
372
+ | 17 | Crawl store create | ? |
373
+ | 18 | Crawl job verify | ? |
374
+ | 19 | Crawl job:cancel | ? |
375
+ | 20 | Crawl store delete | ? |
376
+ | 21 | Hook registration | ? |
377
+ | 22 | PostToolUse - library | ? |
378
+ | 23 | PostToolUse - non-library | ? |
379
+ | 24 | Skill activation - match | ? |
380
+ | 25 | Skill activation - exclude | ? |
381
+ | 26 | Skill rules file | ? |
382
+ | 27 | Delete test store | ? |
383
+ | 28 | Remove test content | ? |
384
+ | 29 | Verify store cleanup | ? |
385
+ | 30 | Verify no artifacts | ? |
386
+
387
+ **Result: X/30 tests passed**
388
+
389
+ ### Development mode (24 tests):
390
+
391
+ Same table but tests 11-16 show "SKIPPED (--dev mode)"
392
+
393
+ **Result: X/24 tests passed (6 skipped)**
320
394
 
321
395
  ---
322
396
 
@@ -324,12 +398,11 @@ These tests verify that plugin hooks work correctly by running hook scripts dire
324
398
 
325
399
  If tests fail partway through, clean up manually:
326
400
 
327
- **In --dev mode:**
328
401
  ```bash
402
+ # Delete test stores
329
403
  node scripts/test-mcp-dev.js call execute '{"command":"store:delete","args":{"store":"bk-test-store"}}' 2>/dev/null || true
404
+ node scripts/test-mcp-dev.js call execute '{"command":"store:delete","args":{"store":"bk-crawl-test"}}' 2>/dev/null || true
405
+
406
+ # Remove test content
330
407
  rm -rf .bluera/bluera-knowledge/test-content
331
408
  ```
332
-
333
- **In production mode:**
334
- 1. Call MCP tool `execute` with `{ command: "store:delete", args: { store: "bk-test-store" } }`
335
- 2. Run: `rm -rf .bluera/bluera-knowledge/test-content`
@@ -4,8 +4,6 @@
4
4
  import { execSync } from "child_process";
5
5
  import {
6
6
  appendFileSync,
7
- chmodSync,
8
- createReadStream,
9
7
  createWriteStream,
10
8
  existsSync,
11
9
  mkdirSync,
@@ -16,7 +14,6 @@ import { get } from "https";
16
14
  import { arch, homedir, platform } from "os";
17
15
  import { dirname, join } from "path";
18
16
  import { fileURLToPath } from "url";
19
- import { createGunzip } from "zlib";
20
17
  var logDir = join(homedir(), ".bluera", "bluera-knowledge", "logs");
21
18
  var logFile = join(logDir, "app.log");
22
19
  var log = (level, msg, data) => {
@@ -115,107 +112,6 @@ function downloadFile(url, destPath) {
115
112
  request(url);
116
113
  });
117
114
  }
118
- function extractTar(tarPath, destDir) {
119
- return new Promise((resolve, reject) => {
120
- const gunzip = createGunzip();
121
- const input = createReadStream(tarPath);
122
- let buffer = Buffer.alloc(0);
123
- let currentFile = null;
124
- let bytesRemaining = 0;
125
- let fileStream = null;
126
- const parseHeader = (header) => {
127
- if (header.every((b) => b === 0)) {
128
- return null;
129
- }
130
- const name = header.subarray(0, 100).toString("utf-8").replace(/\0/g, "");
131
- const mode = parseInt(header.subarray(100, 108).toString("utf-8").trim(), 8);
132
- const size = parseInt(header.subarray(124, 136).toString("utf-8").trim(), 8);
133
- const typeByte = header[156];
134
- const typeFlag = typeByte !== void 0 ? String.fromCharCode(typeByte) : "0";
135
- let type = "file";
136
- if (typeFlag === "5") type = "directory";
137
- else if (typeFlag === "2") type = "symlink";
138
- return { name, size, type, mode: isNaN(mode) ? 420 : mode };
139
- };
140
- const processChunk = (chunk) => {
141
- buffer = Buffer.concat([buffer, chunk]);
142
- while (buffer.length > 0) {
143
- if (currentFile === null) {
144
- if (buffer.length < 512) break;
145
- const header = buffer.subarray(0, 512);
146
- buffer = buffer.subarray(512);
147
- const parsed = parseHeader(header);
148
- if (parsed === null) {
149
- continue;
150
- }
151
- currentFile = parsed;
152
- bytesRemaining = currentFile.size;
153
- let filePath = currentFile.name;
154
- if (filePath.startsWith("./")) {
155
- filePath = filePath.slice(2);
156
- }
157
- if (filePath.length === 0 || filePath === ".") {
158
- currentFile = null;
159
- continue;
160
- }
161
- const fullPath = join(destDir, filePath);
162
- if (currentFile.type === "directory") {
163
- mkdirSync(fullPath, { recursive: true });
164
- currentFile = null;
165
- } else if (currentFile.type === "file" && bytesRemaining > 0) {
166
- mkdirSync(dirname(fullPath), { recursive: true });
167
- fileStream = createWriteStream(fullPath, {
168
- mode: currentFile.mode
169
- });
170
- } else if (currentFile.type === "file" && bytesRemaining === 0) {
171
- mkdirSync(dirname(fullPath), { recursive: true });
172
- createWriteStream(fullPath, { mode: currentFile.mode }).close();
173
- currentFile = null;
174
- } else {
175
- currentFile = null;
176
- }
177
- } else {
178
- const toRead = Math.min(bytesRemaining, buffer.length);
179
- if (fileStream !== null && toRead > 0) {
180
- fileStream.write(buffer.subarray(0, toRead));
181
- }
182
- buffer = buffer.subarray(toRead);
183
- bytesRemaining -= toRead;
184
- if (bytesRemaining === 0) {
185
- if (fileStream !== null) {
186
- const stream = fileStream;
187
- const file = currentFile;
188
- stream.end(() => {
189
- if ((file.mode & 73) !== 0) {
190
- try {
191
- const fullPath = join(
192
- destDir,
193
- file.name.startsWith("./") ? file.name.slice(2) : file.name
194
- );
195
- chmodSync(fullPath, file.mode);
196
- } catch {
197
- }
198
- }
199
- });
200
- fileStream = null;
201
- }
202
- currentFile = null;
203
- const padding = (512 - toRead % 512) % 512;
204
- if (buffer.length >= padding) {
205
- buffer = buffer.subarray(padding);
206
- }
207
- }
208
- }
209
- }
210
- };
211
- input.pipe(gunzip).on("data", processChunk).on("end", () => {
212
- if (fileStream !== null) {
213
- fileStream.end();
214
- }
215
- resolve();
216
- }).on("error", reject);
217
- });
218
- }
219
115
  function isManifest(value) {
220
116
  return typeof value === "object" && value !== null && "version" in value && "platforms" in value && typeof value.platforms === "object";
221
117
  }
@@ -241,7 +137,7 @@ async function downloadPrebuilt() {
241
137
  const tarPath = join(tmpDir, `bluera-knowledge-${platformKey}.tar.gz`);
242
138
  await downloadFile(platformInfo.url, tarPath);
243
139
  log("info", "Download complete, extracting", { tarPath, destDir: pluginRoot });
244
- await extractTar(tarPath, pluginRoot);
140
+ execSync(`tar -xzf "${tarPath}" -C "${pluginRoot}"`, { stdio: "pipe" });
245
141
  try {
246
142
  unlinkSync(tarPath);
247
143
  } catch {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/mcp/bootstrap.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * MCP Server Bootstrap - installs dependencies before starting server.\n *\n * Uses only Node.js built-ins (no external dependencies required).\n * Self-locates plugin root via import.meta.url (doesn't rely on CLAUDE_PLUGIN_ROOT).\n *\n * Dependency installation strategy:\n * 1. Fast path: node_modules already exists → skip\n * 2. Try prebuilt: Download platform-specific tarball from GitHub release\n * 3. Package manager: Run bun install or npm ci if prebuilt unavailable\n *\n * IMPORTANT: MCP servers must NOT log to stderr - Claude Code treats stderr output\n * as an error and may mark the MCP server as failed. All logging goes to file.\n */\nimport { execSync } from 'node:child_process';\nimport {\n appendFileSync,\n chmodSync,\n createReadStream,\n createWriteStream,\n existsSync,\n mkdirSync,\n readFileSync,\n unlinkSync,\n} from 'node:fs';\nimport { get } from 'node:https';\nimport { arch, homedir, platform } from 'node:os';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { createGunzip } from 'node:zlib';\n\n// Logging helper - writes to file since MCP servers must NOT use stderr\n// (Claude Code treats stderr as error and may fail the server)\n// JSON format matches pino output for consistency\nconst logDir = join(homedir(), '.bluera', 'bluera-knowledge', 'logs');\nconst logFile = join(logDir, 'app.log');\n\nconst log = (\n level: 'info' | 'error' | 'debug',\n msg: string,\n data?: Record<string, unknown>\n): void => {\n try {\n mkdirSync(logDir, { recursive: true });\n const entry = {\n time: new Date().toISOString(),\n level,\n module: 'bootstrap',\n msg,\n ...data,\n };\n appendFileSync(logFile, `${JSON.stringify(entry)}\\n`);\n } catch {\n // Silently fail - we cannot use stderr for MCP servers\n }\n};\n\n// Self-locate plugin root from this file's path\n// dist/mcp/bootstrap.js -> plugin root (two directories up)\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\nconst pluginRoot = join(__dirname, '..', '..');\n\n// Get version from package.json\nconst getVersion = (): string => {\n try {\n const pkg: unknown = JSON.parse(readFileSync(join(pluginRoot, 'package.json'), 'utf-8'));\n if (\n typeof pkg === 'object' &&\n pkg !== null &&\n 'version' in pkg &&\n typeof pkg.version === 'string'\n ) {\n return `v${pkg.version}`;\n }\n return 'unknown';\n } catch {\n return 'unknown';\n }\n};\n\nconst VERSION = getVersion();\nconst MANIFEST_URL = `https://github.com/blueraai/bluera-knowledge/releases/download/${VERSION}/manifest.json`;\n\n/**\n * Fetch JSON from URL with redirect handling.\n * Uses only Node.js built-ins.\n */\nfunction fetchJSON(url: string): Promise<unknown> {\n return new Promise((resolve, reject) => {\n const request = (targetUrl: string, redirectCount = 0): void => {\n if (redirectCount > 5) {\n reject(new Error('Too many redirects'));\n return;\n }\n\n get(targetUrl, { headers: { 'User-Agent': 'bluera-knowledge' } }, (res) => {\n // Follow redirects\n const location = res.headers.location;\n if (\n (res.statusCode === 302 || res.statusCode === 301) &&\n typeof location === 'string' &&\n location.length > 0\n ) {\n request(location, redirectCount + 1);\n return;\n }\n const statusCode = res.statusCode ?? 0;\n if (statusCode !== 200) {\n reject(new Error(`HTTP ${String(statusCode)}`));\n return;\n }\n let data = '';\n res.on('data', (chunk: Buffer) => (data += chunk.toString()));\n res.on('end', () => {\n try {\n resolve(JSON.parse(data));\n } catch (e) {\n reject(e instanceof Error ? e : new Error(String(e)));\n }\n });\n }).on('error', reject);\n };\n request(url);\n });\n}\n\n/**\n * Download file from URL to destination path.\n * Uses only Node.js built-ins.\n */\nfunction downloadFile(url: string, destPath: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const request = (targetUrl: string, redirectCount = 0): void => {\n if (redirectCount > 10) {\n reject(new Error('Too many redirects'));\n return;\n }\n\n get(targetUrl, { headers: { 'User-Agent': 'bluera-knowledge' } }, (res) => {\n const location = res.headers.location;\n if (\n (res.statusCode === 302 || res.statusCode === 301) &&\n typeof location === 'string' &&\n location.length > 0\n ) {\n request(location, redirectCount + 1);\n return;\n }\n const statusCode = res.statusCode ?? 0;\n if (statusCode !== 200) {\n reject(new Error(`HTTP ${String(statusCode)}`));\n return;\n }\n const file = createWriteStream(destPath);\n res.pipe(file);\n file.on('finish', () => {\n file.close();\n resolve();\n });\n file.on('error', (err) => {\n file.close();\n reject(err);\n });\n }).on('error', reject);\n };\n request(url);\n });\n}\n\n/**\n * Simple tar parser using Node.js built-ins.\n *\n * TAR format:\n * - 512-byte header blocks\n * - File data follows, padded to 512-byte boundary\n * - Two empty 512-byte blocks at end\n */\nfunction extractTar(tarPath: string, destDir: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const gunzip = createGunzip();\n const input = createReadStream(tarPath);\n\n let buffer = Buffer.alloc(0);\n let currentFile: {\n name: string;\n size: number;\n type: string;\n mode: number;\n } | null = null;\n let bytesRemaining = 0;\n let fileStream: ReturnType<typeof createWriteStream> | null = null;\n\n const parseHeader = (\n header: Buffer\n ): { name: string; size: number; type: string; mode: number } | null => {\n // Check for empty block (end of archive)\n if (header.every((b) => b === 0)) {\n return null;\n }\n\n // Parse tar header fields\n const name = header.subarray(0, 100).toString('utf-8').replace(/\\0/g, '');\n const mode = parseInt(header.subarray(100, 108).toString('utf-8').trim(), 8);\n const size = parseInt(header.subarray(124, 136).toString('utf-8').trim(), 8);\n const typeByte = header[156];\n const typeFlag = typeByte !== undefined ? String.fromCharCode(typeByte) : '0';\n\n // Type: '0' or '' = file, '5' = directory, '2' = symlink\n let type = 'file';\n if (typeFlag === '5') type = 'directory';\n else if (typeFlag === '2') type = 'symlink';\n\n return { name, size, type, mode: isNaN(mode) ? 0o644 : mode };\n };\n\n const processChunk = (chunk: Buffer): void => {\n buffer = Buffer.concat([buffer, chunk]);\n\n while (buffer.length > 0) {\n if (currentFile === null) {\n // Need to read a header\n if (buffer.length < 512) break;\n\n const header = buffer.subarray(0, 512);\n buffer = buffer.subarray(512);\n\n const parsed = parseHeader(header);\n if (parsed === null) {\n // End of archive\n continue;\n }\n\n currentFile = parsed;\n bytesRemaining = currentFile.size;\n\n // Skip ./ prefix and handle path\n let filePath = currentFile.name;\n if (filePath.startsWith('./')) {\n filePath = filePath.slice(2);\n }\n if (filePath.length === 0 || filePath === '.') {\n currentFile = null;\n continue;\n }\n\n const fullPath = join(destDir, filePath);\n\n if (currentFile.type === 'directory') {\n mkdirSync(fullPath, { recursive: true });\n currentFile = null;\n } else if (currentFile.type === 'file' && bytesRemaining > 0) {\n mkdirSync(dirname(fullPath), { recursive: true });\n fileStream = createWriteStream(fullPath, {\n mode: currentFile.mode,\n });\n } else if (currentFile.type === 'file' && bytesRemaining === 0) {\n // Empty file\n mkdirSync(dirname(fullPath), { recursive: true });\n createWriteStream(fullPath, { mode: currentFile.mode }).close();\n currentFile = null;\n } else {\n // Skip symlinks and other types\n currentFile = null;\n }\n } else {\n // Reading file content\n const toRead = Math.min(bytesRemaining, buffer.length);\n if (fileStream !== null && toRead > 0) {\n fileStream.write(buffer.subarray(0, toRead));\n }\n buffer = buffer.subarray(toRead);\n bytesRemaining -= toRead;\n\n if (bytesRemaining === 0) {\n if (fileStream !== null) {\n const stream = fileStream;\n const file = currentFile;\n stream.end(() => {\n // Set executable bit for scripts\n if ((file.mode & 0o111) !== 0) {\n try {\n const fullPath = join(\n destDir,\n file.name.startsWith('./') ? file.name.slice(2) : file.name\n );\n chmodSync(fullPath, file.mode);\n } catch {\n // Ignore chmod errors\n }\n }\n });\n fileStream = null;\n }\n currentFile = null;\n\n // Skip padding to 512-byte boundary\n const padding = (512 - (toRead % 512)) % 512;\n if (buffer.length >= padding) {\n buffer = buffer.subarray(padding);\n }\n }\n }\n }\n };\n\n input\n .pipe(gunzip)\n .on('data', processChunk)\n .on('end', () => {\n if (fileStream !== null) {\n fileStream.end();\n }\n resolve();\n })\n .on('error', reject);\n });\n}\n\ninterface PlatformInfo {\n url: string;\n sha256: string;\n}\n\ninterface Manifest {\n version: string;\n platforms: Record<string, PlatformInfo>;\n}\n\nfunction isManifest(value: unknown): value is Manifest {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'version' in value &&\n 'platforms' in value &&\n typeof value.platforms === 'object'\n );\n}\n\n/**\n * Try to download and extract prebuilt binary for current platform.\n * Returns true if successful, false if prebuilt is not available.\n */\nasync function downloadPrebuilt(): Promise<boolean> {\n const plat = platform(); // 'darwin', 'linux', 'win32'\n const ar = arch(); // 'arm64', 'x64'\n const platformKey = `${plat}-${ar}`;\n\n try {\n log('info', 'Checking for prebuilt binary', { platformKey, version: VERSION });\n\n const manifestData = await fetchJSON(MANIFEST_URL);\n if (!isManifest(manifestData)) {\n log('info', 'Invalid manifest format');\n return false;\n }\n\n const platformInfo = manifestData.platforms[platformKey];\n if (platformInfo === undefined) {\n log('info', 'No prebuilt binary available for platform', { platformKey });\n return false;\n }\n\n log('info', 'Downloading prebuilt binary', { url: platformInfo.url });\n const tmpDir = join(homedir(), '.bluera', 'tmp');\n mkdirSync(tmpDir, { recursive: true });\n const tarPath = join(tmpDir, `bluera-knowledge-${platformKey}.tar.gz`);\n\n await downloadFile(platformInfo.url, tarPath);\n log('info', 'Download complete, extracting', { tarPath, destDir: pluginRoot });\n\n await extractTar(tarPath, pluginRoot);\n\n // Cleanup temp file\n try {\n unlinkSync(tarPath);\n } catch {\n // Ignore cleanup errors\n }\n\n log('info', 'Prebuilt binary installed successfully');\n return true;\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n log('debug', 'Prebuilt download failed, will use package manager', {\n error: message,\n });\n return false;\n }\n}\n\n/**\n * Install dependencies using bun or npm.\n */\nfunction installWithPackageManager(): void {\n const hasBun = ((): boolean => {\n try {\n execSync('which bun', { stdio: 'ignore' });\n return true;\n } catch {\n return false;\n }\n })();\n\n const cmd = hasBun ? 'bun install --frozen-lockfile' : 'npm ci --silent';\n log('info', 'Installing dependencies with package manager', { hasBun, cmd });\n execSync(cmd, { cwd: pluginRoot, stdio: 'inherit' });\n log('info', 'Dependencies installed via package manager');\n}\n\n/**\n * Ensure dependencies are available.\n * Tries prebuilt first, falls back to package manager.\n */\nasync function ensureDependencies(): Promise<void> {\n // Fast path: already installed\n if (existsSync(join(pluginRoot, 'node_modules'))) {\n log('info', 'Dependencies already installed');\n return;\n }\n\n // Try prebuilt binary first (faster, no npm install needed)\n const prebuiltSuccess = await downloadPrebuilt();\n if (prebuiltSuccess) {\n return;\n }\n\n // Prebuilt not available, use package manager instead\n installWithPackageManager();\n}\n\n// Main entry point\nlog('info', 'Bootstrap starting', { pluginRoot, version: VERSION });\n\nawait ensureDependencies();\n\n// Now that dependencies are installed, import and run the server\n// Dynamic import required because @modelcontextprotocol/sdk wouldn't be available before install\nlog('info', 'Loading server module');\nconst { runMCPServer } = await import('./server.js');\n\nconst projectRoot = process.env['PROJECT_ROOT'];\nif (projectRoot === undefined) {\n throw new Error('PROJECT_ROOT environment variable is required');\n}\n\nlog('info', 'Starting MCP server', {\n projectRoot,\n dataDir: process.env['DATA_DIR'],\n});\n\nawait runMCPServer({\n dataDir: process.env['DATA_DIR'],\n config: process.env['CONFIG_PATH'],\n projectRoot,\n});\n"],"mappings":";;;AAeA,SAAS,gBAAgB;AACzB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,WAAW;AACpB,SAAS,MAAM,SAAS,gBAAgB;AACxC,SAAS,SAAS,YAAY;AAC9B,SAAS,qBAAqB;AAC9B,SAAS,oBAAoB;AAK7B,IAAM,SAAS,KAAK,QAAQ,GAAG,WAAW,oBAAoB,MAAM;AACpE,IAAM,UAAU,KAAK,QAAQ,SAAS;AAEtC,IAAM,MAAM,CACV,OACA,KACA,SACS;AACT,MAAI;AACF,cAAU,QAAQ,EAAE,WAAW,KAAK,CAAC;AACrC,UAAM,QAAQ;AAAA,MACZ,OAAM,oBAAI,KAAK,GAAE,YAAY;AAAA,MAC7B;AAAA,MACA,QAAQ;AAAA,MACR;AAAA,MACA,GAAG;AAAA,IACL;AACA,mBAAe,SAAS,GAAG,KAAK,UAAU,KAAK,CAAC;AAAA,CAAI;AAAA,EACtD,QAAQ;AAAA,EAER;AACF;AAIA,IAAM,aAAa,cAAc,YAAY,GAAG;AAChD,IAAM,YAAY,QAAQ,UAAU;AACpC,IAAM,aAAa,KAAK,WAAW,MAAM,IAAI;AAG7C,IAAM,aAAa,MAAc;AAC/B,MAAI;AACF,UAAM,MAAe,KAAK,MAAM,aAAa,KAAK,YAAY,cAAc,GAAG,OAAO,CAAC;AACvF,QACE,OAAO,QAAQ,YACf,QAAQ,QACR,aAAa,OACb,OAAO,IAAI,YAAY,UACvB;AACA,aAAO,IAAI,IAAI,OAAO;AAAA,IACxB;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,IAAM,UAAU,WAAW;AAC3B,IAAM,eAAe,kEAAkE,OAAO;AAM9F,SAAS,UAAU,KAA+B;AAChD,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,UAAU,CAAC,WAAmB,gBAAgB,MAAY;AAC9D,UAAI,gBAAgB,GAAG;AACrB,eAAO,IAAI,MAAM,oBAAoB,CAAC;AACtC;AAAA,MACF;AAEA,UAAI,WAAW,EAAE,SAAS,EAAE,cAAc,mBAAmB,EAAE,GAAG,CAAC,QAAQ;AAEzE,cAAM,WAAW,IAAI,QAAQ;AAC7B,aACG,IAAI,eAAe,OAAO,IAAI,eAAe,QAC9C,OAAO,aAAa,YACpB,SAAS,SAAS,GAClB;AACA,kBAAQ,UAAU,gBAAgB,CAAC;AACnC;AAAA,QACF;AACA,cAAM,aAAa,IAAI,cAAc;AACrC,YAAI,eAAe,KAAK;AACtB,iBAAO,IAAI,MAAM,QAAQ,OAAO,UAAU,CAAC,EAAE,CAAC;AAC9C;AAAA,QACF;AACA,YAAI,OAAO;AACX,YAAI,GAAG,QAAQ,CAAC,UAAmB,QAAQ,MAAM,SAAS,CAAE;AAC5D,YAAI,GAAG,OAAO,MAAM;AAClB,cAAI;AACF,oBAAQ,KAAK,MAAM,IAAI,CAAC;AAAA,UAC1B,SAAS,GAAG;AACV,mBAAO,aAAa,QAAQ,IAAI,IAAI,MAAM,OAAO,CAAC,CAAC,CAAC;AAAA,UACtD;AAAA,QACF,CAAC;AAAA,MACH,CAAC,EAAE,GAAG,SAAS,MAAM;AAAA,IACvB;AACA,YAAQ,GAAG;AAAA,EACb,CAAC;AACH;AAMA,SAAS,aAAa,KAAa,UAAiC;AAClE,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,UAAU,CAAC,WAAmB,gBAAgB,MAAY;AAC9D,UAAI,gBAAgB,IAAI;AACtB,eAAO,IAAI,MAAM,oBAAoB,CAAC;AACtC;AAAA,MACF;AAEA,UAAI,WAAW,EAAE,SAAS,EAAE,cAAc,mBAAmB,EAAE,GAAG,CAAC,QAAQ;AACzE,cAAM,WAAW,IAAI,QAAQ;AAC7B,aACG,IAAI,eAAe,OAAO,IAAI,eAAe,QAC9C,OAAO,aAAa,YACpB,SAAS,SAAS,GAClB;AACA,kBAAQ,UAAU,gBAAgB,CAAC;AACnC;AAAA,QACF;AACA,cAAM,aAAa,IAAI,cAAc;AACrC,YAAI,eAAe,KAAK;AACtB,iBAAO,IAAI,MAAM,QAAQ,OAAO,UAAU,CAAC,EAAE,CAAC;AAC9C;AAAA,QACF;AACA,cAAM,OAAO,kBAAkB,QAAQ;AACvC,YAAI,KAAK,IAAI;AACb,aAAK,GAAG,UAAU,MAAM;AACtB,eAAK,MAAM;AACX,kBAAQ;AAAA,QACV,CAAC;AACD,aAAK,GAAG,SAAS,CAAC,QAAQ;AACxB,eAAK,MAAM;AACX,iBAAO,GAAG;AAAA,QACZ,CAAC;AAAA,MACH,CAAC,EAAE,GAAG,SAAS,MAAM;AAAA,IACvB;AACA,YAAQ,GAAG;AAAA,EACb,CAAC;AACH;AAUA,SAAS,WAAW,SAAiB,SAAgC;AACnE,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,SAAS,aAAa;AAC5B,UAAM,QAAQ,iBAAiB,OAAO;AAEtC,QAAI,SAAS,OAAO,MAAM,CAAC;AAC3B,QAAI,cAKO;AACX,QAAI,iBAAiB;AACrB,QAAI,aAA0D;AAE9D,UAAM,cAAc,CAClB,WACsE;AAEtE,UAAI,OAAO,MAAM,CAAC,MAAM,MAAM,CAAC,GAAG;AAChC,eAAO;AAAA,MACT;AAGA,YAAM,OAAO,OAAO,SAAS,GAAG,GAAG,EAAE,SAAS,OAAO,EAAE,QAAQ,OAAO,EAAE;AACxE,YAAM,OAAO,SAAS,OAAO,SAAS,KAAK,GAAG,EAAE,SAAS,OAAO,EAAE,KAAK,GAAG,CAAC;AAC3E,YAAM,OAAO,SAAS,OAAO,SAAS,KAAK,GAAG,EAAE,SAAS,OAAO,EAAE,KAAK,GAAG,CAAC;AAC3E,YAAM,WAAW,OAAO,GAAG;AAC3B,YAAM,WAAW,aAAa,SAAY,OAAO,aAAa,QAAQ,IAAI;AAG1E,UAAI,OAAO;AACX,UAAI,aAAa,IAAK,QAAO;AAAA,eACpB,aAAa,IAAK,QAAO;AAElC,aAAO,EAAE,MAAM,MAAM,MAAM,MAAM,MAAM,IAAI,IAAI,MAAQ,KAAK;AAAA,IAC9D;AAEA,UAAM,eAAe,CAAC,UAAwB;AAC5C,eAAS,OAAO,OAAO,CAAC,QAAQ,KAAK,CAAC;AAEtC,aAAO,OAAO,SAAS,GAAG;AACxB,YAAI,gBAAgB,MAAM;AAExB,cAAI,OAAO,SAAS,IAAK;AAEzB,gBAAM,SAAS,OAAO,SAAS,GAAG,GAAG;AACrC,mBAAS,OAAO,SAAS,GAAG;AAE5B,gBAAM,SAAS,YAAY,MAAM;AACjC,cAAI,WAAW,MAAM;AAEnB;AAAA,UACF;AAEA,wBAAc;AACd,2BAAiB,YAAY;AAG7B,cAAI,WAAW,YAAY;AAC3B,cAAI,SAAS,WAAW,IAAI,GAAG;AAC7B,uBAAW,SAAS,MAAM,CAAC;AAAA,UAC7B;AACA,cAAI,SAAS,WAAW,KAAK,aAAa,KAAK;AAC7C,0BAAc;AACd;AAAA,UACF;AAEA,gBAAM,WAAW,KAAK,SAAS,QAAQ;AAEvC,cAAI,YAAY,SAAS,aAAa;AACpC,sBAAU,UAAU,EAAE,WAAW,KAAK,CAAC;AACvC,0BAAc;AAAA,UAChB,WAAW,YAAY,SAAS,UAAU,iBAAiB,GAAG;AAC5D,sBAAU,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAChD,yBAAa,kBAAkB,UAAU;AAAA,cACvC,MAAM,YAAY;AAAA,YACpB,CAAC;AAAA,UACH,WAAW,YAAY,SAAS,UAAU,mBAAmB,GAAG;AAE9D,sBAAU,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAChD,8BAAkB,UAAU,EAAE,MAAM,YAAY,KAAK,CAAC,EAAE,MAAM;AAC9D,0BAAc;AAAA,UAChB,OAAO;AAEL,0BAAc;AAAA,UAChB;AAAA,QACF,OAAO;AAEL,gBAAM,SAAS,KAAK,IAAI,gBAAgB,OAAO,MAAM;AACrD,cAAI,eAAe,QAAQ,SAAS,GAAG;AACrC,uBAAW,MAAM,OAAO,SAAS,GAAG,MAAM,CAAC;AAAA,UAC7C;AACA,mBAAS,OAAO,SAAS,MAAM;AAC/B,4BAAkB;AAElB,cAAI,mBAAmB,GAAG;AACxB,gBAAI,eAAe,MAAM;AACvB,oBAAM,SAAS;AACf,oBAAM,OAAO;AACb,qBAAO,IAAI,MAAM;AAEf,qBAAK,KAAK,OAAO,QAAW,GAAG;AAC7B,sBAAI;AACF,0BAAM,WAAW;AAAA,sBACf;AAAA,sBACA,KAAK,KAAK,WAAW,IAAI,IAAI,KAAK,KAAK,MAAM,CAAC,IAAI,KAAK;AAAA,oBACzD;AACA,8BAAU,UAAU,KAAK,IAAI;AAAA,kBAC/B,QAAQ;AAAA,kBAER;AAAA,gBACF;AAAA,cACF,CAAC;AACD,2BAAa;AAAA,YACf;AACA,0BAAc;AAGd,kBAAM,WAAW,MAAO,SAAS,OAAQ;AACzC,gBAAI,OAAO,UAAU,SAAS;AAC5B,uBAAS,OAAO,SAAS,OAAO;AAAA,YAClC;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,UACG,KAAK,MAAM,EACX,GAAG,QAAQ,YAAY,EACvB,GAAG,OAAO,MAAM;AACf,UAAI,eAAe,MAAM;AACvB,mBAAW,IAAI;AAAA,MACjB;AACA,cAAQ;AAAA,IACV,CAAC,EACA,GAAG,SAAS,MAAM;AAAA,EACvB,CAAC;AACH;AAYA,SAAS,WAAW,OAAmC;AACrD,SACE,OAAO,UAAU,YACjB,UAAU,QACV,aAAa,SACb,eAAe,SACf,OAAO,MAAM,cAAc;AAE/B;AAMA,eAAe,mBAAqC;AAClD,QAAM,OAAO,SAAS;AACtB,QAAM,KAAK,KAAK;AAChB,QAAM,cAAc,GAAG,IAAI,IAAI,EAAE;AAEjC,MAAI;AACF,QAAI,QAAQ,gCAAgC,EAAE,aAAa,SAAS,QAAQ,CAAC;AAE7E,UAAM,eAAe,MAAM,UAAU,YAAY;AACjD,QAAI,CAAC,WAAW,YAAY,GAAG;AAC7B,UAAI,QAAQ,yBAAyB;AACrC,aAAO;AAAA,IACT;AAEA,UAAM,eAAe,aAAa,UAAU,WAAW;AACvD,QAAI,iBAAiB,QAAW;AAC9B,UAAI,QAAQ,6CAA6C,EAAE,YAAY,CAAC;AACxE,aAAO;AAAA,IACT;AAEA,QAAI,QAAQ,+BAA+B,EAAE,KAAK,aAAa,IAAI,CAAC;AACpE,UAAM,SAAS,KAAK,QAAQ,GAAG,WAAW,KAAK;AAC/C,cAAU,QAAQ,EAAE,WAAW,KAAK,CAAC;AACrC,UAAM,UAAU,KAAK,QAAQ,oBAAoB,WAAW,SAAS;AAErE,UAAM,aAAa,aAAa,KAAK,OAAO;AAC5C,QAAI,QAAQ,iCAAiC,EAAE,SAAS,SAAS,WAAW,CAAC;AAE7E,UAAM,WAAW,SAAS,UAAU;AAGpC,QAAI;AACF,iBAAW,OAAO;AAAA,IACpB,QAAQ;AAAA,IAER;AAEA,QAAI,QAAQ,wCAAwC;AACpD,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,QAAI,SAAS,sDAAsD;AAAA,MACjE,OAAO;AAAA,IACT,CAAC;AACD,WAAO;AAAA,EACT;AACF;AAKA,SAAS,4BAAkC;AACzC,QAAM,UAAU,MAAe;AAC7B,QAAI;AACF,eAAS,aAAa,EAAE,OAAO,SAAS,CAAC;AACzC,aAAO;AAAA,IACT,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF,GAAG;AAEH,QAAM,MAAM,SAAS,kCAAkC;AACvD,MAAI,QAAQ,gDAAgD,EAAE,QAAQ,IAAI,CAAC;AAC3E,WAAS,KAAK,EAAE,KAAK,YAAY,OAAO,UAAU,CAAC;AACnD,MAAI,QAAQ,4CAA4C;AAC1D;AAMA,eAAe,qBAAoC;AAEjD,MAAI,WAAW,KAAK,YAAY,cAAc,CAAC,GAAG;AAChD,QAAI,QAAQ,gCAAgC;AAC5C;AAAA,EACF;AAGA,QAAM,kBAAkB,MAAM,iBAAiB;AAC/C,MAAI,iBAAiB;AACnB;AAAA,EACF;AAGA,4BAA0B;AAC5B;AAGA,IAAI,QAAQ,sBAAsB,EAAE,YAAY,SAAS,QAAQ,CAAC;AAElE,MAAM,mBAAmB;AAIzB,IAAI,QAAQ,uBAAuB;AACnC,IAAM,EAAE,aAAa,IAAI,MAAM,OAAO,aAAa;AAEnD,IAAM,cAAc,QAAQ,IAAI,cAAc;AAC9C,IAAI,gBAAgB,QAAW;AAC7B,QAAM,IAAI,MAAM,+CAA+C;AACjE;AAEA,IAAI,QAAQ,uBAAuB;AAAA,EACjC;AAAA,EACA,SAAS,QAAQ,IAAI,UAAU;AACjC,CAAC;AAED,MAAM,aAAa;AAAA,EACjB,SAAS,QAAQ,IAAI,UAAU;AAAA,EAC/B,QAAQ,QAAQ,IAAI,aAAa;AAAA,EACjC;AACF,CAAC;","names":[]}
1
+ {"version":3,"sources":["../../src/mcp/bootstrap.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * MCP Server Bootstrap - installs dependencies before starting server.\n *\n * Uses only Node.js built-ins (no external dependencies required).\n * Self-locates plugin root via import.meta.url (doesn't rely on CLAUDE_PLUGIN_ROOT).\n *\n * Dependency installation strategy:\n * 1. Fast path: node_modules already exists → skip\n * 2. Try prebuilt: Download platform-specific tarball from GitHub release\n * 3. Package manager: Run bun install or npm ci if prebuilt unavailable\n *\n * IMPORTANT: MCP servers must NOT log to stderr - Claude Code treats stderr output\n * as an error and may mark the MCP server as failed. All logging goes to file.\n */\nimport { execSync } from 'node:child_process';\nimport {\n appendFileSync,\n createWriteStream,\n existsSync,\n mkdirSync,\n readFileSync,\n unlinkSync,\n} from 'node:fs';\nimport { get } from 'node:https';\nimport { arch, homedir, platform } from 'node:os';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\n// Logging helper - writes to file since MCP servers must NOT use stderr\n// (Claude Code treats stderr as error and may fail the server)\n// JSON format matches pino output for consistency\nconst logDir = join(homedir(), '.bluera', 'bluera-knowledge', 'logs');\nconst logFile = join(logDir, 'app.log');\n\nconst log = (\n level: 'info' | 'error' | 'debug',\n msg: string,\n data?: Record<string, unknown>\n): void => {\n try {\n mkdirSync(logDir, { recursive: true });\n const entry = {\n time: new Date().toISOString(),\n level,\n module: 'bootstrap',\n msg,\n ...data,\n };\n appendFileSync(logFile, `${JSON.stringify(entry)}\\n`);\n } catch {\n // Silently fail - we cannot use stderr for MCP servers\n }\n};\n\n// Self-locate plugin root from this file's path\n// dist/mcp/bootstrap.js -> plugin root (two directories up)\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\nconst pluginRoot = join(__dirname, '..', '..');\n\n// Get version from package.json\nconst getVersion = (): string => {\n try {\n const pkg: unknown = JSON.parse(readFileSync(join(pluginRoot, 'package.json'), 'utf-8'));\n if (\n typeof pkg === 'object' &&\n pkg !== null &&\n 'version' in pkg &&\n typeof pkg.version === 'string'\n ) {\n return `v${pkg.version}`;\n }\n return 'unknown';\n } catch {\n return 'unknown';\n }\n};\n\nconst VERSION = getVersion();\nconst MANIFEST_URL = `https://github.com/blueraai/bluera-knowledge/releases/download/${VERSION}/manifest.json`;\n\n/**\n * Fetch JSON from URL with redirect handling.\n * Uses only Node.js built-ins.\n */\nfunction fetchJSON(url: string): Promise<unknown> {\n return new Promise((resolve, reject) => {\n const request = (targetUrl: string, redirectCount = 0): void => {\n if (redirectCount > 5) {\n reject(new Error('Too many redirects'));\n return;\n }\n\n get(targetUrl, { headers: { 'User-Agent': 'bluera-knowledge' } }, (res) => {\n // Follow redirects\n const location = res.headers.location;\n if (\n (res.statusCode === 302 || res.statusCode === 301) &&\n typeof location === 'string' &&\n location.length > 0\n ) {\n request(location, redirectCount + 1);\n return;\n }\n const statusCode = res.statusCode ?? 0;\n if (statusCode !== 200) {\n reject(new Error(`HTTP ${String(statusCode)}`));\n return;\n }\n let data = '';\n res.on('data', (chunk: Buffer) => (data += chunk.toString()));\n res.on('end', () => {\n try {\n resolve(JSON.parse(data));\n } catch (e) {\n reject(e instanceof Error ? e : new Error(String(e)));\n }\n });\n }).on('error', reject);\n };\n request(url);\n });\n}\n\n/**\n * Download file from URL to destination path.\n * Uses only Node.js built-ins.\n */\nfunction downloadFile(url: string, destPath: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const request = (targetUrl: string, redirectCount = 0): void => {\n if (redirectCount > 10) {\n reject(new Error('Too many redirects'));\n return;\n }\n\n get(targetUrl, { headers: { 'User-Agent': 'bluera-knowledge' } }, (res) => {\n const location = res.headers.location;\n if (\n (res.statusCode === 302 || res.statusCode === 301) &&\n typeof location === 'string' &&\n location.length > 0\n ) {\n request(location, redirectCount + 1);\n return;\n }\n const statusCode = res.statusCode ?? 0;\n if (statusCode !== 200) {\n reject(new Error(`HTTP ${String(statusCode)}`));\n return;\n }\n const file = createWriteStream(destPath);\n res.pipe(file);\n file.on('finish', () => {\n file.close();\n resolve();\n });\n file.on('error', (err) => {\n file.close();\n reject(err);\n });\n }).on('error', reject);\n };\n request(url);\n });\n}\n\ninterface PlatformInfo {\n url: string;\n sha256: string;\n}\n\ninterface Manifest {\n version: string;\n platforms: Record<string, PlatformInfo>;\n}\n\nfunction isManifest(value: unknown): value is Manifest {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'version' in value &&\n 'platforms' in value &&\n typeof value.platforms === 'object'\n );\n}\n\n/**\n * Try to download and extract prebuilt binary for current platform.\n * Returns true if successful, false if prebuilt is not available.\n */\nasync function downloadPrebuilt(): Promise<boolean> {\n const plat = platform(); // 'darwin', 'linux', 'win32'\n const ar = arch(); // 'arm64', 'x64'\n const platformKey = `${plat}-${ar}`;\n\n try {\n log('info', 'Checking for prebuilt binary', { platformKey, version: VERSION });\n\n const manifestData = await fetchJSON(MANIFEST_URL);\n if (!isManifest(manifestData)) {\n log('info', 'Invalid manifest format');\n return false;\n }\n\n const platformInfo = manifestData.platforms[platformKey];\n if (platformInfo === undefined) {\n log('info', 'No prebuilt binary available for platform', { platformKey });\n return false;\n }\n\n log('info', 'Downloading prebuilt binary', { url: platformInfo.url });\n const tmpDir = join(homedir(), '.bluera', 'tmp');\n mkdirSync(tmpDir, { recursive: true });\n const tarPath = join(tmpDir, `bluera-knowledge-${platformKey}.tar.gz`);\n\n await downloadFile(platformInfo.url, tarPath);\n log('info', 'Download complete, extracting', { tarPath, destDir: pluginRoot });\n\n // Use system tar - handles all formats (PAX, GNU, USTAR) correctly\n execSync(`tar -xzf \"${tarPath}\" -C \"${pluginRoot}\"`, { stdio: 'pipe' });\n\n // Cleanup temp file\n try {\n unlinkSync(tarPath);\n } catch {\n // Ignore cleanup errors\n }\n\n log('info', 'Prebuilt binary installed successfully');\n return true;\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n log('debug', 'Prebuilt download failed, will use package manager', {\n error: message,\n });\n return false;\n }\n}\n\n/**\n * Install dependencies using bun or npm.\n */\nfunction installWithPackageManager(): void {\n const hasBun = ((): boolean => {\n try {\n execSync('which bun', { stdio: 'ignore' });\n return true;\n } catch {\n return false;\n }\n })();\n\n const cmd = hasBun ? 'bun install --frozen-lockfile' : 'npm ci --silent';\n log('info', 'Installing dependencies with package manager', { hasBun, cmd });\n execSync(cmd, { cwd: pluginRoot, stdio: 'inherit' });\n log('info', 'Dependencies installed via package manager');\n}\n\n/**\n * Ensure dependencies are available.\n * Tries prebuilt first, falls back to package manager.\n */\nasync function ensureDependencies(): Promise<void> {\n // Fast path: already installed\n if (existsSync(join(pluginRoot, 'node_modules'))) {\n log('info', 'Dependencies already installed');\n return;\n }\n\n // Try prebuilt binary first (faster, no npm install needed)\n const prebuiltSuccess = await downloadPrebuilt();\n if (prebuiltSuccess) {\n return;\n }\n\n // Prebuilt not available, use package manager instead\n installWithPackageManager();\n}\n\n// Main entry point\nlog('info', 'Bootstrap starting', { pluginRoot, version: VERSION });\n\nawait ensureDependencies();\n\n// Now that dependencies are installed, import and run the server\n// Dynamic import required because @modelcontextprotocol/sdk wouldn't be available before install\nlog('info', 'Loading server module');\nconst { runMCPServer } = await import('./server.js');\n\nconst projectRoot = process.env['PROJECT_ROOT'];\nif (projectRoot === undefined) {\n throw new Error('PROJECT_ROOT environment variable is required');\n}\n\nlog('info', 'Starting MCP server', {\n projectRoot,\n dataDir: process.env['DATA_DIR'],\n});\n\nawait runMCPServer({\n dataDir: process.env['DATA_DIR'],\n config: process.env['CONFIG_PATH'],\n projectRoot,\n});\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bluera-knowledge",
3
- "version": "0.16.2",
3
+ "version": "0.16.4",
4
4
  "description": "CLI tool for managing knowledge stores with semantic search",
5
5
  "type": "module",
6
6
  "bin": {