bluera-knowledge 0.9.31 → 0.9.34

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.
Files changed (200) hide show
  1. package/.claude/commands/code-review.md +15 -0
  2. package/.claude/hooks/post-edit-check.sh +5 -3
  3. package/.claude/skills/atomic-commits/SKILL.md +3 -1
  4. package/.claude/skills/code-review-repo/skill.md +62 -0
  5. package/.husky/pre-commit +3 -2
  6. package/.prettierrc +9 -0
  7. package/.versionrc.json +1 -1
  8. package/CHANGELOG.md +35 -0
  9. package/CLAUDE.md +6 -0
  10. package/README.md +25 -13
  11. package/bun.lock +277 -33
  12. package/dist/{chunk-L2YVNC63.js → chunk-6FHWC36B.js} +9 -1
  13. package/dist/chunk-6FHWC36B.js.map +1 -0
  14. package/dist/{chunk-2SJHNRXD.js → chunk-DC7CGSGT.js} +288 -241
  15. package/dist/chunk-DC7CGSGT.js.map +1 -0
  16. package/dist/{chunk-RWSXP3PQ.js → chunk-WFNPNAAP.js} +3194 -3024
  17. package/dist/chunk-WFNPNAAP.js.map +1 -0
  18. package/dist/{chunk-OGEY66FZ.js → chunk-Z2KKVH45.js} +548 -482
  19. package/dist/chunk-Z2KKVH45.js.map +1 -0
  20. package/dist/index.js +871 -754
  21. package/dist/index.js.map +1 -1
  22. package/dist/mcp/server.js +3 -3
  23. package/dist/watch.service-BJV3TI3F.js +7 -0
  24. package/dist/workers/background-worker-cli.js +46 -45
  25. package/dist/workers/background-worker-cli.js.map +1 -1
  26. package/eslint.config.js +43 -1
  27. package/package.json +18 -11
  28. package/plugin.json +8 -0
  29. package/python/requirements.txt +1 -1
  30. package/src/analysis/ast-parser.test.ts +12 -11
  31. package/src/analysis/ast-parser.ts +28 -22
  32. package/src/analysis/code-graph.test.ts +52 -62
  33. package/src/analysis/code-graph.ts +9 -13
  34. package/src/analysis/dependency-usage-analyzer.test.ts +91 -271
  35. package/src/analysis/dependency-usage-analyzer.ts +52 -24
  36. package/src/analysis/go-ast-parser.test.ts +22 -22
  37. package/src/analysis/go-ast-parser.ts +18 -25
  38. package/src/analysis/parser-factory.test.ts +9 -9
  39. package/src/analysis/parser-factory.ts +3 -3
  40. package/src/analysis/python-ast-parser.test.ts +27 -27
  41. package/src/analysis/python-ast-parser.ts +2 -2
  42. package/src/analysis/repo-url-resolver.test.ts +82 -82
  43. package/src/analysis/rust-ast-parser.test.ts +19 -19
  44. package/src/analysis/rust-ast-parser.ts +17 -27
  45. package/src/analysis/tree-sitter-parser.test.ts +3 -3
  46. package/src/analysis/tree-sitter-parser.ts +10 -16
  47. package/src/cli/commands/crawl.test.ts +40 -24
  48. package/src/cli/commands/crawl.ts +186 -161
  49. package/src/cli/commands/index-cmd.test.ts +90 -90
  50. package/src/cli/commands/index-cmd.ts +52 -36
  51. package/src/cli/commands/mcp.test.ts +6 -6
  52. package/src/cli/commands/mcp.ts +2 -2
  53. package/src/cli/commands/plugin-api.test.ts +16 -18
  54. package/src/cli/commands/plugin-api.ts +9 -6
  55. package/src/cli/commands/search.test.ts +16 -7
  56. package/src/cli/commands/search.ts +124 -87
  57. package/src/cli/commands/serve.test.ts +67 -25
  58. package/src/cli/commands/serve.ts +18 -3
  59. package/src/cli/commands/setup.test.ts +176 -101
  60. package/src/cli/commands/setup.ts +140 -117
  61. package/src/cli/commands/store.test.ts +82 -53
  62. package/src/cli/commands/store.ts +56 -37
  63. package/src/cli/program.ts +2 -2
  64. package/src/crawl/article-converter.test.ts +4 -1
  65. package/src/crawl/article-converter.ts +46 -31
  66. package/src/crawl/bridge.test.ts +240 -132
  67. package/src/crawl/bridge.ts +87 -30
  68. package/src/crawl/claude-client.test.ts +124 -56
  69. package/src/crawl/claude-client.ts +7 -15
  70. package/src/crawl/intelligent-crawler.test.ts +65 -22
  71. package/src/crawl/intelligent-crawler.ts +86 -53
  72. package/src/crawl/markdown-utils.ts +1 -4
  73. package/src/db/embeddings.ts +4 -6
  74. package/src/db/lance.test.ts +63 -4
  75. package/src/db/lance.ts +31 -12
  76. package/src/index.ts +26 -17
  77. package/src/logging/index.ts +1 -5
  78. package/src/logging/logger.ts +3 -5
  79. package/src/logging/payload.test.ts +1 -1
  80. package/src/logging/payload.ts +3 -5
  81. package/src/mcp/commands/index.ts +2 -2
  82. package/src/mcp/commands/job.commands.ts +12 -18
  83. package/src/mcp/commands/meta.commands.ts +13 -13
  84. package/src/mcp/commands/registry.ts +5 -8
  85. package/src/mcp/commands/store.commands.ts +19 -19
  86. package/src/mcp/handlers/execute.handler.test.ts +10 -10
  87. package/src/mcp/handlers/execute.handler.ts +4 -5
  88. package/src/mcp/handlers/index.ts +10 -14
  89. package/src/mcp/handlers/job.handler.test.ts +10 -10
  90. package/src/mcp/handlers/job.handler.ts +22 -25
  91. package/src/mcp/handlers/search.handler.test.ts +36 -65
  92. package/src/mcp/handlers/search.handler.ts +135 -104
  93. package/src/mcp/handlers/store.handler.test.ts +41 -52
  94. package/src/mcp/handlers/store.handler.ts +108 -88
  95. package/src/mcp/schemas/index.test.ts +73 -68
  96. package/src/mcp/schemas/index.ts +18 -12
  97. package/src/mcp/server.test.ts +1 -1
  98. package/src/mcp/server.ts +59 -46
  99. package/src/plugin/commands.test.ts +230 -95
  100. package/src/plugin/commands.ts +24 -25
  101. package/src/plugin/dependency-analyzer.test.ts +52 -52
  102. package/src/plugin/dependency-analyzer.ts +85 -22
  103. package/src/plugin/git-clone.test.ts +24 -13
  104. package/src/plugin/git-clone.ts +3 -7
  105. package/src/server/app.test.ts +109 -109
  106. package/src/server/app.ts +32 -23
  107. package/src/server/index.test.ts +64 -66
  108. package/src/services/chunking.service.test.ts +32 -32
  109. package/src/services/chunking.service.ts +16 -9
  110. package/src/services/code-graph.service.test.ts +30 -36
  111. package/src/services/code-graph.service.ts +24 -10
  112. package/src/services/code-unit.service.test.ts +55 -11
  113. package/src/services/code-unit.service.ts +85 -11
  114. package/src/services/config.service.test.ts +37 -18
  115. package/src/services/config.service.ts +30 -7
  116. package/src/services/index.service.test.ts +49 -18
  117. package/src/services/index.service.ts +98 -48
  118. package/src/services/index.ts +8 -10
  119. package/src/services/job.service.test.ts +22 -22
  120. package/src/services/job.service.ts +18 -18
  121. package/src/services/project-root.service.test.ts +1 -3
  122. package/src/services/search.service.test.ts +248 -120
  123. package/src/services/search.service.ts +286 -156
  124. package/src/services/services.test.ts +36 -0
  125. package/src/services/snippet.service.test.ts +14 -6
  126. package/src/services/snippet.service.ts +7 -5
  127. package/src/services/store.service.test.ts +68 -29
  128. package/src/services/store.service.ts +41 -12
  129. package/src/services/watch.service.test.ts +34 -14
  130. package/src/services/watch.service.ts +11 -1
  131. package/src/types/brands.test.ts +3 -1
  132. package/src/types/index.ts +2 -13
  133. package/src/types/search.ts +10 -8
  134. package/src/utils/type-guards.test.ts +20 -15
  135. package/src/utils/type-guards.ts +1 -1
  136. package/src/workers/background-worker-cli.ts +2 -2
  137. package/src/workers/background-worker.test.ts +54 -40
  138. package/src/workers/background-worker.ts +76 -60
  139. package/src/workers/spawn-worker.test.ts +22 -10
  140. package/src/workers/spawn-worker.ts +6 -6
  141. package/tests/analysis/ast-parser.test.ts +3 -3
  142. package/tests/analysis/code-graph.test.ts +5 -5
  143. package/tests/fixtures/code-snippets/api/error-handling.ts +4 -15
  144. package/tests/fixtures/code-snippets/api/rest-controller.ts +3 -9
  145. package/tests/fixtures/code-snippets/auth/jwt-auth.ts +5 -21
  146. package/tests/fixtures/code-snippets/auth/oauth-flow.ts +4 -4
  147. package/tests/fixtures/code-snippets/database/repository-pattern.ts +11 -3
  148. package/tests/fixtures/corpus/oss-repos/hono/src/adapter/aws-lambda/handler.ts +2 -2
  149. package/tests/fixtures/corpus/oss-repos/hono/src/adapter/cloudflare-pages/handler.ts +1 -1
  150. package/tests/fixtures/corpus/oss-repos/hono/src/adapter/cloudflare-workers/serve-static.ts +2 -2
  151. package/tests/fixtures/corpus/oss-repos/hono/src/client/client.ts +2 -2
  152. package/tests/fixtures/corpus/oss-repos/hono/src/client/types.ts +22 -20
  153. package/tests/fixtures/corpus/oss-repos/hono/src/context.ts +13 -10
  154. package/tests/fixtures/corpus/oss-repos/hono/src/helper/accepts/accepts.ts +10 -7
  155. package/tests/fixtures/corpus/oss-repos/hono/src/helper/adapter/index.ts +2 -2
  156. package/tests/fixtures/corpus/oss-repos/hono/src/helper/css/index.ts +1 -1
  157. package/tests/fixtures/corpus/oss-repos/hono/src/helper/factory/index.ts +16 -16
  158. package/tests/fixtures/corpus/oss-repos/hono/src/helper/ssg/ssg.ts +2 -2
  159. package/tests/fixtures/corpus/oss-repos/hono/src/hono-base.ts +3 -3
  160. package/tests/fixtures/corpus/oss-repos/hono/src/hono.ts +1 -1
  161. package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/css.ts +2 -2
  162. package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/intrinsic-element/components.ts +1 -1
  163. package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/render.ts +7 -7
  164. package/tests/fixtures/corpus/oss-repos/hono/src/jsx/hooks/index.ts +3 -3
  165. package/tests/fixtures/corpus/oss-repos/hono/src/jsx/intrinsic-element/components.ts +1 -1
  166. package/tests/fixtures/corpus/oss-repos/hono/src/jsx/utils.ts +6 -6
  167. package/tests/fixtures/corpus/oss-repos/hono/src/middleware/jsx-renderer/index.ts +3 -3
  168. package/tests/fixtures/corpus/oss-repos/hono/src/middleware/serve-static/index.ts +1 -1
  169. package/tests/fixtures/corpus/oss-repos/hono/src/preset/quick.ts +1 -1
  170. package/tests/fixtures/corpus/oss-repos/hono/src/preset/tiny.ts +1 -1
  171. package/tests/fixtures/corpus/oss-repos/hono/src/router/pattern-router/router.ts +2 -2
  172. package/tests/fixtures/corpus/oss-repos/hono/src/router/reg-exp-router/node.ts +4 -4
  173. package/tests/fixtures/corpus/oss-repos/hono/src/router/reg-exp-router/router.ts +1 -1
  174. package/tests/fixtures/corpus/oss-repos/hono/src/router/trie-router/node.ts +1 -1
  175. package/tests/fixtures/corpus/oss-repos/hono/src/types.ts +166 -169
  176. package/tests/fixtures/corpus/oss-repos/hono/src/utils/body.ts +8 -8
  177. package/tests/fixtures/corpus/oss-repos/hono/src/utils/color.ts +3 -3
  178. package/tests/fixtures/corpus/oss-repos/hono/src/utils/cookie.ts +2 -2
  179. package/tests/fixtures/corpus/oss-repos/hono/src/utils/encode.ts +2 -2
  180. package/tests/fixtures/corpus/oss-repos/hono/src/utils/types.ts +30 -33
  181. package/tests/fixtures/corpus/oss-repos/hono/src/validator/validator.ts +2 -2
  182. package/tests/fixtures/test-server.ts +3 -2
  183. package/tests/helpers/performance-metrics.ts +8 -25
  184. package/tests/helpers/search-relevance.ts +14 -69
  185. package/tests/integration/cli-consistency.test.ts +5 -4
  186. package/tests/integration/e2e-workflow.test.ts +2 -0
  187. package/tests/integration/python-bridge.test.ts +13 -3
  188. package/tests/mcp/server.test.ts +1 -1
  189. package/tests/services/code-unit.service.test.ts +48 -0
  190. package/tests/services/job.service.test.ts +124 -0
  191. package/tests/services/search.progressive-context.test.ts +2 -2
  192. package/.claude-plugin/plugin.json +0 -13
  193. package/BUGS-FOUND.md +0 -71
  194. package/dist/chunk-2SJHNRXD.js.map +0 -1
  195. package/dist/chunk-L2YVNC63.js.map +0 -1
  196. package/dist/chunk-OGEY66FZ.js.map +0 -1
  197. package/dist/chunk-RWSXP3PQ.js.map +0 -1
  198. package/dist/watch.service-YAIKKDCF.js +0 -7
  199. package/skills/atomic-commits/SKILL.md +0 -77
  200. /package/dist/{watch.service-YAIKKDCF.js.map → watch.service-BJV3TI3F.js.map} +0 -0
@@ -1,15 +1,11 @@
1
- import type { ToolHandler, ToolResponse } from '../types.js';
2
- import type {
3
- CheckJobStatusArgs,
4
- ListJobsArgs,
5
- CancelJobArgs
6
- } from '../schemas/index.js';
1
+ import { JobService } from '../../services/job.service.js';
7
2
  import {
8
3
  CheckJobStatusArgsSchema,
9
4
  ListJobsArgsSchema,
10
- CancelJobArgsSchema
5
+ CancelJobArgsSchema,
11
6
  } from '../schemas/index.js';
12
- import { JobService } from '../../services/job.service.js';
7
+ import type { CheckJobStatusArgs, ListJobsArgs, CancelJobArgs } from '../schemas/index.js';
8
+ import type { ToolHandler, ToolResponse } from '../types.js';
13
9
 
14
10
  /**
15
11
  * Handle check_job_status requests
@@ -36,9 +32,9 @@ export const handleCheckJobStatus: ToolHandler<CheckJobStatusArgs> = (
36
32
  content: [
37
33
  {
38
34
  type: 'text',
39
- text: JSON.stringify(job, null, 2)
40
- }
41
- ]
35
+ text: JSON.stringify(job, null, 2),
36
+ },
37
+ ],
42
38
  });
43
39
  };
44
40
 
@@ -47,10 +43,7 @@ export const handleCheckJobStatus: ToolHandler<CheckJobStatusArgs> = (
47
43
  *
48
44
  * Lists all jobs with optional filtering by status or active status.
49
45
  */
50
- export const handleListJobs: ToolHandler<ListJobsArgs> = (
51
- args,
52
- context
53
- ): Promise<ToolResponse> => {
46
+ export const handleListJobs: ToolHandler<ListJobsArgs> = (args, context): Promise<ToolResponse> => {
54
47
  // Validate arguments with Zod
55
48
  const validated = ListJobsArgsSchema.parse(args);
56
49
 
@@ -71,9 +64,9 @@ export const handleListJobs: ToolHandler<ListJobsArgs> = (
71
64
  content: [
72
65
  {
73
66
  type: 'text',
74
- text: JSON.stringify({ jobs }, null, 2)
75
- }
76
- ]
67
+ text: JSON.stringify({ jobs }, null, 2),
68
+ },
69
+ ],
77
70
  });
78
71
  };
79
72
 
@@ -105,12 +98,16 @@ export const handleCancelJob: ToolHandler<CancelJobArgs> = (
105
98
  content: [
106
99
  {
107
100
  type: 'text',
108
- text: JSON.stringify({
109
- success: true,
110
- job,
111
- message: 'Job cancelled successfully'
112
- }, null, 2)
113
- }
114
- ]
101
+ text: JSON.stringify(
102
+ {
103
+ success: true,
104
+ job,
105
+ message: 'Job cancelled successfully',
106
+ },
107
+ null,
108
+ 2
109
+ ),
110
+ },
111
+ ],
115
112
  });
116
113
  };
@@ -13,7 +13,7 @@ function parseSearchResponse(text: string): { header: string; json: Record<strin
13
13
  const jsonStr = parts.slice(1).join('\n\n');
14
14
  return {
15
15
  header,
16
- json: JSON.parse(jsonStr || '{}')
16
+ json: JSON.parse(jsonStr || '{}'),
17
17
  };
18
18
  }
19
19
 
@@ -28,18 +28,16 @@ describe('Search Handlers', () => {
28
28
  // Create mock services
29
29
  mockServices = {
30
30
  store: {
31
- list: vi.fn().mockResolvedValue([
32
- { id: 'store1', name: 'Test Store', type: 'file' }
33
- ]),
31
+ list: vi.fn().mockResolvedValue([{ id: 'store1', name: 'Test Store', type: 'file' }]),
34
32
  getByIdOrName: vi.fn().mockResolvedValue({
35
33
  id: 'store1',
36
34
  name: 'Test Store',
37
35
  type: 'file',
38
- path: '/test/path'
39
- })
36
+ path: '/test/path',
37
+ }),
40
38
  },
41
39
  lance: {
42
- initialize: vi.fn().mockResolvedValue(undefined)
40
+ initialize: vi.fn().mockResolvedValue(undefined),
43
41
  },
44
42
  search: {
45
43
  search: vi.fn().mockResolvedValue({
@@ -49,19 +47,19 @@ describe('Search Handlers', () => {
49
47
  score: 0.95,
50
48
  content: 'test content for search',
51
49
  metadata: { storeId: 'store1' },
52
- summary: { file: 'test.ts', line: 1 }
53
- }
50
+ summary: { file: 'test.ts', line: 1 },
51
+ },
54
52
  ],
55
53
  totalResults: 1,
56
54
  mode: 'hybrid',
57
- timeMs: 50
58
- })
59
- }
55
+ timeMs: 50,
56
+ }),
57
+ },
60
58
  } as unknown as ServiceContainer;
61
59
 
62
60
  mockContext = {
63
61
  services: mockServices,
64
- options: {}
62
+ options: {},
65
63
  };
66
64
  });
67
65
 
@@ -80,7 +78,7 @@ describe('Search Handlers', () => {
80
78
  expect(mockServices.search.search).toHaveBeenCalledWith(
81
79
  expect.objectContaining({
82
80
  query: 'test query',
83
- stores: ['store1']
81
+ stores: ['store1'],
84
82
  })
85
83
  );
86
84
 
@@ -113,10 +111,7 @@ describe('Search Handlers', () => {
113
111
  });
114
112
 
115
113
  it('should initialize all stores', async () => {
116
- await handleSearch(
117
- { query: 'test', detail: 'minimal', limit: 10 },
118
- mockContext
119
- );
114
+ await handleSearch({ query: 'test', detail: 'minimal', limit: 10 }, mockContext);
120
115
 
121
116
  expect(mockServices.lance.initialize).toHaveBeenCalledWith('store1');
122
117
  });
@@ -127,18 +122,12 @@ describe('Search Handlers', () => {
127
122
  );
128
123
 
129
124
  await expect(
130
- handleSearch(
131
- { query: 'test', detail: 'minimal', limit: 10 },
132
- mockContext
133
- )
125
+ handleSearch({ query: 'test', detail: 'minimal', limit: 10 }, mockContext)
134
126
  ).rejects.toThrow('Failed to initialize vector stores');
135
127
  });
136
128
 
137
129
  it('should cache search results', async () => {
138
- await handleSearch(
139
- { query: 'test', detail: 'minimal', limit: 10 },
140
- mockContext
141
- );
130
+ await handleSearch({ query: 'test', detail: 'minimal', limit: 10 }, mockContext);
142
131
 
143
132
  // Verify cache was populated
144
133
  const cached = resultCache.get('doc1');
@@ -166,7 +155,7 @@ describe('Search Handlers', () => {
166
155
  createdAt: new Date(),
167
156
  updatedAt: new Date(),
168
157
  status: 'ready',
169
- description: 'Test'
158
+ description: 'Test',
170
159
  });
171
160
 
172
161
  const result = await handleSearch(
@@ -221,7 +210,7 @@ describe('Search Handlers', () => {
221
210
  score: 0.95,
222
211
  content: 'test content for get full context',
223
212
  metadata: { storeId: 'store1', docId: 'doc1' },
224
- summary: { file: 'test.ts', line: 1 }
213
+ summary: { file: 'test.ts', line: 1 },
225
214
  });
226
215
  });
227
216
 
@@ -233,13 +222,10 @@ describe('Search Handlers', () => {
233
222
  content: 'test content',
234
223
  metadata: { storeId: 'store1', docId: 'doc1' },
235
224
  summary: { file: 'test.ts', line: 1 },
236
- full: { code: 'full code here' }
225
+ full: { code: 'full code here' },
237
226
  });
238
227
 
239
- const result = await handleGetFullContext(
240
- { resultId: 'doc1' },
241
- mockContext
242
- );
228
+ const result = await handleGetFullContext({ resultId: 'doc1' }, mockContext);
243
229
 
244
230
  const response = JSON.parse(result.content[0]?.text ?? '{}');
245
231
  expect(response.id).toBe('doc1');
@@ -251,12 +237,9 @@ describe('Search Handlers', () => {
251
237
  });
252
238
 
253
239
  it('should throw if result not in cache', async () => {
254
- await expect(
255
- handleGetFullContext(
256
- { resultId: 'nonexistent' },
257
- mockContext
258
- )
259
- ).rejects.toThrow('Result not found in cache');
240
+ await expect(handleGetFullContext({ resultId: 'nonexistent' }, mockContext)).rejects.toThrow(
241
+ 'Result not found in cache'
242
+ );
260
243
  });
261
244
 
262
245
  it('should re-query for full context if not already full', async () => {
@@ -268,25 +251,22 @@ describe('Search Handlers', () => {
268
251
  content: 'test content',
269
252
  metadata: { storeId: 'store1', docId: 'doc1' },
270
253
  summary: { file: 'test.ts', line: 1 },
271
- full: { code: 'full code from re-query' }
272
- }
254
+ full: { code: 'full code from re-query' },
255
+ },
273
256
  ],
274
257
  totalResults: 1,
275
258
  mode: 'hybrid',
276
- timeMs: 50
259
+ timeMs: 50,
277
260
  });
278
261
 
279
- const result = await handleGetFullContext(
280
- { resultId: 'doc1' },
281
- mockContext
282
- );
262
+ const result = await handleGetFullContext({ resultId: 'doc1' }, mockContext);
283
263
 
284
264
  expect(mockServices.store.getByIdOrName).toHaveBeenCalledWith('store1');
285
265
  expect(mockServices.lance.initialize).toHaveBeenCalledWith('store1');
286
266
  expect(mockServices.search.search).toHaveBeenCalledWith(
287
267
  expect.objectContaining({
288
268
  detail: 'full',
289
- limit: 1
269
+ limit: 1,
290
270
  })
291
271
  );
292
272
 
@@ -297,12 +277,9 @@ describe('Search Handlers', () => {
297
277
  it('should throw if store not found', async () => {
298
278
  vi.mocked(mockServices.store.getByIdOrName).mockResolvedValue(undefined);
299
279
 
300
- await expect(
301
- handleGetFullContext(
302
- { resultId: 'doc1' },
303
- mockContext
304
- )
305
- ).rejects.toThrow('Store not found');
280
+ await expect(handleGetFullContext({ resultId: 'doc1' }, mockContext)).rejects.toThrow(
281
+ 'Store not found'
282
+ );
306
283
  });
307
284
 
308
285
  it('should return cached result with warning if re-query fails', async () => {
@@ -310,13 +287,10 @@ describe('Search Handlers', () => {
310
287
  results: [], // No matching result found
311
288
  totalResults: 0,
312
289
  mode: 'hybrid',
313
- timeMs: 50
290
+ timeMs: 50,
314
291
  });
315
292
 
316
- const result = await handleGetFullContext(
317
- { resultId: 'doc1' },
318
- mockContext
319
- );
293
+ const result = await handleGetFullContext({ resultId: 'doc1' }, mockContext);
320
294
 
321
295
  const response = JSON.parse(result.content[0]?.text ?? '{}');
322
296
  expect(response.id).toBe('doc1');
@@ -332,18 +306,15 @@ describe('Search Handlers', () => {
332
306
  content: 'test content',
333
307
  metadata: { storeId: 'store1', docId: 'doc1' },
334
308
  summary: { file: 'test.ts', line: 1 },
335
- full: { code: 'full code from re-query' }
336
- }
309
+ full: { code: 'full code from re-query' },
310
+ },
337
311
  ],
338
312
  totalResults: 1,
339
313
  mode: 'hybrid',
340
- timeMs: 50
314
+ timeMs: 50,
341
315
  });
342
316
 
343
- await handleGetFullContext(
344
- { resultId: 'doc1' },
345
- mockContext
346
- );
317
+ await handleGetFullContext({ resultId: 'doc1' }, mockContext);
347
318
 
348
319
  const cached = resultCache.get('doc1');
349
320
  expect(cached?.full).toBeDefined();
@@ -1,11 +1,11 @@
1
- import type { ToolHandler, ToolResponse } from '../types.js';
2
- import type { SearchArgs, GetFullContextArgs } from '../schemas/index.js';
1
+ import { createLogger, summarizePayload } from '../../logging/index.js';
2
+ import { estimateTokens, formatTokenCount } from '../../services/token.service.js';
3
+ import { LRUCache } from '../cache.js';
3
4
  import { SearchArgsSchema, GetFullContextArgsSchema } from '../schemas/index.js';
4
5
  import type { SearchQuery, DocumentId, StoreId } from '../../types/index.js';
5
- import { LRUCache } from '../cache.js';
6
6
  import type { SearchResult } from '../../types/search.js';
7
- import { createLogger, summarizePayload } from '../../logging/index.js';
8
- import { estimateTokens, formatTokenCount } from '../../services/token.service.js';
7
+ import type { SearchArgs, GetFullContextArgs } from '../schemas/index.js';
8
+ import type { ToolHandler, ToolResponse } from '../types.js';
9
9
 
10
10
  const logger = createLogger('mcp-search');
11
11
 
@@ -26,27 +26,33 @@ export const handleSearch: ToolHandler<SearchArgs> = async (
26
26
  // Validate arguments with Zod
27
27
  const validated = SearchArgsSchema.parse(args);
28
28
 
29
- logger.info({
30
- query: validated.query,
31
- stores: validated.stores,
32
- detail: validated.detail,
33
- limit: validated.limit,
34
- intent: validated.intent,
35
- }, 'Search started');
29
+ logger.info(
30
+ {
31
+ query: validated.query,
32
+ stores: validated.stores,
33
+ detail: validated.detail,
34
+ limit: validated.limit,
35
+ intent: validated.intent,
36
+ },
37
+ 'Search started'
38
+ );
36
39
 
37
40
  const { services } = context;
38
41
 
39
42
  // Get all stores if none specified, resolve store names to IDs
40
- const storeIds: StoreId[] = validated.stores !== undefined
41
- ? await Promise.all(validated.stores.map(async (s) => {
42
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
43
- const store = await services.store.getByIdOrName(s as StoreId);
44
- if (!store) {
45
- throw new Error(`Store not found: ${s}`);
46
- }
47
- return store.id;
48
- }))
49
- : (await services.store.list()).map(s => s.id);
43
+ const storeIds: StoreId[] =
44
+ validated.stores !== undefined
45
+ ? await Promise.all(
46
+ validated.stores.map(async (s) => {
47
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
48
+ const store = await services.store.getByIdOrName(s as StoreId);
49
+ if (!store) {
50
+ throw new Error(`Store not found: ${s}`);
51
+ }
52
+ return store.id;
53
+ })
54
+ )
55
+ : (await services.store.list()).map((s) => s.id);
50
56
 
51
57
  // Initialize stores with error handling
52
58
  try {
@@ -65,7 +71,7 @@ export const handleSearch: ToolHandler<SearchArgs> = async (
65
71
  stores: storeIds,
66
72
  mode: 'hybrid',
67
73
  limit: validated.limit,
68
- detail: validated.detail
74
+ detail: validated.detail,
69
75
  };
70
76
 
71
77
  const results = await services.search.search(searchQuery);
@@ -76,29 +82,35 @@ export const handleSearch: ToolHandler<SearchArgs> = async (
76
82
  }
77
83
 
78
84
  // Add repoRoot to results for cloned repos
79
- const enhancedResults = await Promise.all(results.results.map(async (r) => {
80
- const storeId = r.metadata.storeId;
81
- const store = await services.store.getByIdOrName(storeId);
82
-
83
- return {
84
- id: r.id,
85
- score: r.score,
86
- summary: {
87
- ...r.summary,
88
- storeName: store?.name,
89
- repoRoot: store !== undefined && store.type === 'repo' ? store.path : undefined
90
- },
91
- context: r.context,
92
- full: r.full
93
- };
94
- }));
95
-
96
- const responseJson = JSON.stringify({
97
- results: enhancedResults,
98
- totalResults: results.totalResults,
99
- mode: results.mode,
100
- timeMs: results.timeMs
101
- }, null, 2);
85
+ const enhancedResults = await Promise.all(
86
+ results.results.map(async (r) => {
87
+ const storeId = r.metadata.storeId;
88
+ const store = await services.store.getByIdOrName(storeId);
89
+
90
+ return {
91
+ id: r.id,
92
+ score: r.score,
93
+ summary: {
94
+ ...r.summary,
95
+ storeName: store?.name,
96
+ repoRoot: store?.type === 'repo' ? store.path : undefined,
97
+ },
98
+ context: r.context,
99
+ full: r.full,
100
+ };
101
+ })
102
+ );
103
+
104
+ const responseJson = JSON.stringify(
105
+ {
106
+ results: enhancedResults,
107
+ totalResults: results.totalResults,
108
+ mode: results.mode,
109
+ timeMs: results.timeMs,
110
+ },
111
+ null,
112
+ 2
113
+ );
102
114
 
103
115
  // Calculate actual token estimate based on response content
104
116
  const responseTokens = estimateTokens(responseJson);
@@ -107,21 +119,24 @@ export const handleSearch: ToolHandler<SearchArgs> = async (
107
119
  const header = `Search: "${validated.query}" | Results: ${String(results.totalResults)} | ${formatTokenCount(responseTokens)} tokens | ${String(results.timeMs)}ms\n\n`;
108
120
 
109
121
  // Log the complete MCP response that will be sent to Claude Code
110
- logger.info({
111
- query: validated.query,
112
- totalResults: results.totalResults,
113
- responseTokens,
114
- timeMs: results.timeMs,
115
- ...summarizePayload(responseJson, 'mcp-response', validated.query),
116
- }, 'Search complete - context sent to Claude Code');
122
+ logger.info(
123
+ {
124
+ query: validated.query,
125
+ totalResults: results.totalResults,
126
+ responseTokens,
127
+ timeMs: results.timeMs,
128
+ ...summarizePayload(responseJson, 'mcp-response', validated.query),
129
+ },
130
+ 'Search complete - context sent to Claude Code'
131
+ );
117
132
 
118
133
  return {
119
134
  content: [
120
135
  {
121
136
  type: 'text',
122
- text: header + responseJson
123
- }
124
- ]
137
+ text: header + responseJson,
138
+ },
139
+ ],
125
140
  };
126
141
  };
127
142
 
@@ -147,35 +162,40 @@ export const handleGetFullContext: ToolHandler<GetFullContextArgs> = async (
147
162
  const cachedResult = resultCache.get(resultId);
148
163
 
149
164
  if (!cachedResult) {
150
- throw new Error(
151
- `Result not found in cache: ${resultId}. Run a search first to cache results.`
152
- );
165
+ throw new Error(`Result not found in cache: ${resultId}. Run a search first to cache results.`);
153
166
  }
154
167
 
155
168
  // If result already has full context, return it
156
169
  if (cachedResult.full) {
157
- const responseJson = JSON.stringify({
158
- id: cachedResult.id,
159
- score: cachedResult.score,
160
- summary: cachedResult.summary,
161
- context: cachedResult.context,
162
- full: cachedResult.full
163
- }, null, 2);
164
-
165
- logger.info({
166
- resultId,
167
- cached: true,
168
- hasFullContext: true,
169
- ...summarizePayload(responseJson, 'mcp-full-context', resultId),
170
- }, 'Full context retrieved from cache');
170
+ const responseJson = JSON.stringify(
171
+ {
172
+ id: cachedResult.id,
173
+ score: cachedResult.score,
174
+ summary: cachedResult.summary,
175
+ context: cachedResult.context,
176
+ full: cachedResult.full,
177
+ },
178
+ null,
179
+ 2
180
+ );
181
+
182
+ logger.info(
183
+ {
184
+ resultId,
185
+ cached: true,
186
+ hasFullContext: true,
187
+ ...summarizePayload(responseJson, 'mcp-full-context', resultId),
188
+ },
189
+ 'Full context retrieved from cache'
190
+ );
171
191
 
172
192
  return {
173
193
  content: [
174
194
  {
175
195
  type: 'text',
176
- text: responseJson
177
- }
178
- ]
196
+ text: responseJson,
197
+ },
198
+ ],
179
199
  };
180
200
  }
181
201
 
@@ -194,13 +214,13 @@ export const handleGetFullContext: ToolHandler<GetFullContextArgs> = async (
194
214
  stores: [store.id],
195
215
  mode: 'hybrid',
196
216
  limit: 1,
197
- detail: 'full'
217
+ detail: 'full',
198
218
  };
199
219
 
200
220
  const results = await services.search.search(searchQuery);
201
221
 
202
222
  // Find matching result by ID
203
- const fullResult = results.results.find(r => r.id === resultId);
223
+ const fullResult = results.results.find((r) => r.id === resultId);
204
224
 
205
225
  if (!fullResult) {
206
226
  // Return cached result even if we couldn't get full detail
@@ -208,42 +228,53 @@ export const handleGetFullContext: ToolHandler<GetFullContextArgs> = async (
208
228
  content: [
209
229
  {
210
230
  type: 'text',
211
- text: JSON.stringify({
212
- id: cachedResult.id,
213
- score: cachedResult.score,
214
- summary: cachedResult.summary,
215
- context: cachedResult.context,
216
- warning: 'Could not retrieve full context, returning cached minimal result'
217
- }, null, 2)
218
- }
219
- ]
231
+ text: JSON.stringify(
232
+ {
233
+ id: cachedResult.id,
234
+ score: cachedResult.score,
235
+ summary: cachedResult.summary,
236
+ context: cachedResult.context,
237
+ warning: 'Could not retrieve full context, returning cached minimal result',
238
+ },
239
+ null,
240
+ 2
241
+ ),
242
+ },
243
+ ],
220
244
  };
221
245
  }
222
246
 
223
247
  // Update cache with full result
224
248
  resultCache.set(resultId, fullResult);
225
249
 
226
- const responseJson = JSON.stringify({
227
- id: fullResult.id,
228
- score: fullResult.score,
229
- summary: fullResult.summary,
230
- context: fullResult.context,
231
- full: fullResult.full
232
- }, null, 2);
233
-
234
- logger.info({
235
- resultId,
236
- cached: false,
237
- hasFullContext: true,
238
- ...summarizePayload(responseJson, 'mcp-full-context', resultId),
239
- }, 'Full context retrieved via re-query');
250
+ const responseJson = JSON.stringify(
251
+ {
252
+ id: fullResult.id,
253
+ score: fullResult.score,
254
+ summary: fullResult.summary,
255
+ context: fullResult.context,
256
+ full: fullResult.full,
257
+ },
258
+ null,
259
+ 2
260
+ );
261
+
262
+ logger.info(
263
+ {
264
+ resultId,
265
+ cached: false,
266
+ hasFullContext: true,
267
+ ...summarizePayload(responseJson, 'mcp-full-context', resultId),
268
+ },
269
+ 'Full context retrieved via re-query'
270
+ );
240
271
 
241
272
  return {
242
273
  content: [
243
274
  {
244
275
  type: 'text',
245
- text: responseJson
246
- }
247
- ]
276
+ text: responseJson,
277
+ },
278
+ ],
248
279
  };
249
280
  };