bluera-knowledge 0.9.32 → 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 (196) hide show
  1. package/.claude/hooks/post-edit-check.sh +5 -3
  2. package/.claude/skills/atomic-commits/SKILL.md +3 -1
  3. package/.husky/pre-commit +3 -2
  4. package/.prettierrc +9 -0
  5. package/.versionrc.json +1 -1
  6. package/CHANGELOG.md +33 -0
  7. package/CLAUDE.md +6 -0
  8. package/README.md +25 -13
  9. package/bun.lock +277 -33
  10. package/dist/{chunk-L2YVNC63.js → chunk-6FHWC36B.js} +9 -1
  11. package/dist/chunk-6FHWC36B.js.map +1 -0
  12. package/dist/{chunk-RST4XGRL.js → chunk-DC7CGSGT.js} +288 -241
  13. package/dist/chunk-DC7CGSGT.js.map +1 -0
  14. package/dist/{chunk-6PBP5DVD.js → chunk-WFNPNAAP.js} +3212 -3054
  15. package/dist/chunk-WFNPNAAP.js.map +1 -0
  16. package/dist/{chunk-WT2DAEO7.js → chunk-Z2KKVH45.js} +548 -482
  17. package/dist/chunk-Z2KKVH45.js.map +1 -0
  18. package/dist/index.js +871 -758
  19. package/dist/index.js.map +1 -1
  20. package/dist/mcp/server.js +3 -3
  21. package/dist/watch.service-BJV3TI3F.js +7 -0
  22. package/dist/workers/background-worker-cli.js +46 -45
  23. package/dist/workers/background-worker-cli.js.map +1 -1
  24. package/eslint.config.js +43 -1
  25. package/package.json +18 -11
  26. package/plugin.json +8 -0
  27. package/python/requirements.txt +1 -1
  28. package/src/analysis/ast-parser.test.ts +12 -11
  29. package/src/analysis/ast-parser.ts +28 -22
  30. package/src/analysis/code-graph.test.ts +52 -62
  31. package/src/analysis/code-graph.ts +9 -13
  32. package/src/analysis/dependency-usage-analyzer.test.ts +91 -271
  33. package/src/analysis/dependency-usage-analyzer.ts +52 -24
  34. package/src/analysis/go-ast-parser.test.ts +22 -22
  35. package/src/analysis/go-ast-parser.ts +18 -25
  36. package/src/analysis/parser-factory.test.ts +9 -9
  37. package/src/analysis/parser-factory.ts +3 -3
  38. package/src/analysis/python-ast-parser.test.ts +27 -27
  39. package/src/analysis/python-ast-parser.ts +2 -2
  40. package/src/analysis/repo-url-resolver.test.ts +82 -82
  41. package/src/analysis/rust-ast-parser.test.ts +19 -19
  42. package/src/analysis/rust-ast-parser.ts +17 -27
  43. package/src/analysis/tree-sitter-parser.test.ts +3 -3
  44. package/src/analysis/tree-sitter-parser.ts +10 -16
  45. package/src/cli/commands/crawl.test.ts +40 -24
  46. package/src/cli/commands/crawl.ts +186 -166
  47. package/src/cli/commands/index-cmd.test.ts +90 -90
  48. package/src/cli/commands/index-cmd.ts +52 -36
  49. package/src/cli/commands/mcp.test.ts +6 -6
  50. package/src/cli/commands/mcp.ts +2 -2
  51. package/src/cli/commands/plugin-api.test.ts +16 -18
  52. package/src/cli/commands/plugin-api.ts +9 -6
  53. package/src/cli/commands/search.test.ts +16 -7
  54. package/src/cli/commands/search.ts +124 -87
  55. package/src/cli/commands/serve.test.ts +67 -25
  56. package/src/cli/commands/serve.ts +18 -3
  57. package/src/cli/commands/setup.test.ts +176 -101
  58. package/src/cli/commands/setup.ts +140 -117
  59. package/src/cli/commands/store.test.ts +82 -53
  60. package/src/cli/commands/store.ts +56 -37
  61. package/src/cli/program.ts +2 -2
  62. package/src/crawl/article-converter.test.ts +4 -1
  63. package/src/crawl/article-converter.ts +46 -31
  64. package/src/crawl/bridge.test.ts +240 -132
  65. package/src/crawl/bridge.ts +87 -30
  66. package/src/crawl/claude-client.test.ts +124 -56
  67. package/src/crawl/claude-client.ts +7 -15
  68. package/src/crawl/intelligent-crawler.test.ts +65 -22
  69. package/src/crawl/intelligent-crawler.ts +86 -53
  70. package/src/crawl/markdown-utils.ts +1 -4
  71. package/src/db/embeddings.ts +4 -6
  72. package/src/db/lance.test.ts +4 -4
  73. package/src/db/lance.ts +16 -12
  74. package/src/index.ts +26 -17
  75. package/src/logging/index.ts +1 -5
  76. package/src/logging/logger.ts +3 -5
  77. package/src/logging/payload.test.ts +1 -1
  78. package/src/logging/payload.ts +3 -5
  79. package/src/mcp/commands/index.ts +2 -2
  80. package/src/mcp/commands/job.commands.ts +12 -18
  81. package/src/mcp/commands/meta.commands.ts +13 -13
  82. package/src/mcp/commands/registry.ts +5 -8
  83. package/src/mcp/commands/store.commands.ts +19 -19
  84. package/src/mcp/handlers/execute.handler.test.ts +10 -10
  85. package/src/mcp/handlers/execute.handler.ts +4 -5
  86. package/src/mcp/handlers/index.ts +10 -14
  87. package/src/mcp/handlers/job.handler.test.ts +10 -10
  88. package/src/mcp/handlers/job.handler.ts +22 -25
  89. package/src/mcp/handlers/search.handler.test.ts +36 -65
  90. package/src/mcp/handlers/search.handler.ts +135 -104
  91. package/src/mcp/handlers/store.handler.test.ts +41 -52
  92. package/src/mcp/handlers/store.handler.ts +108 -88
  93. package/src/mcp/schemas/index.test.ts +73 -68
  94. package/src/mcp/schemas/index.ts +18 -12
  95. package/src/mcp/server.test.ts +1 -1
  96. package/src/mcp/server.ts +59 -46
  97. package/src/plugin/commands.test.ts +230 -95
  98. package/src/plugin/commands.ts +24 -25
  99. package/src/plugin/dependency-analyzer.test.ts +52 -52
  100. package/src/plugin/dependency-analyzer.ts +85 -22
  101. package/src/plugin/git-clone.test.ts +24 -13
  102. package/src/plugin/git-clone.ts +3 -7
  103. package/src/server/app.test.ts +109 -109
  104. package/src/server/app.ts +32 -23
  105. package/src/server/index.test.ts +64 -66
  106. package/src/services/chunking.service.test.ts +32 -32
  107. package/src/services/chunking.service.ts +16 -9
  108. package/src/services/code-graph.service.test.ts +30 -36
  109. package/src/services/code-graph.service.ts +24 -10
  110. package/src/services/code-unit.service.test.ts +55 -11
  111. package/src/services/code-unit.service.ts +85 -11
  112. package/src/services/config.service.test.ts +37 -18
  113. package/src/services/config.service.ts +30 -7
  114. package/src/services/index.service.test.ts +49 -18
  115. package/src/services/index.service.ts +98 -48
  116. package/src/services/index.ts +6 -9
  117. package/src/services/job.service.test.ts +22 -22
  118. package/src/services/job.service.ts +18 -18
  119. package/src/services/project-root.service.test.ts +1 -3
  120. package/src/services/search.service.test.ts +248 -120
  121. package/src/services/search.service.ts +286 -156
  122. package/src/services/services.test.ts +1 -1
  123. package/src/services/snippet.service.test.ts +14 -6
  124. package/src/services/snippet.service.ts +7 -5
  125. package/src/services/store.service.test.ts +68 -29
  126. package/src/services/store.service.ts +41 -12
  127. package/src/services/watch.service.test.ts +34 -14
  128. package/src/services/watch.service.ts +11 -1
  129. package/src/types/brands.test.ts +3 -1
  130. package/src/types/index.ts +2 -13
  131. package/src/types/search.ts +10 -8
  132. package/src/utils/type-guards.test.ts +20 -15
  133. package/src/utils/type-guards.ts +1 -1
  134. package/src/workers/background-worker-cli.ts +2 -2
  135. package/src/workers/background-worker.test.ts +54 -40
  136. package/src/workers/background-worker.ts +76 -60
  137. package/src/workers/spawn-worker.test.ts +22 -10
  138. package/src/workers/spawn-worker.ts +6 -6
  139. package/tests/analysis/ast-parser.test.ts +3 -3
  140. package/tests/analysis/code-graph.test.ts +5 -5
  141. package/tests/fixtures/code-snippets/api/error-handling.ts +4 -15
  142. package/tests/fixtures/code-snippets/api/rest-controller.ts +3 -9
  143. package/tests/fixtures/code-snippets/auth/jwt-auth.ts +5 -21
  144. package/tests/fixtures/code-snippets/auth/oauth-flow.ts +4 -4
  145. package/tests/fixtures/code-snippets/database/repository-pattern.ts +11 -3
  146. package/tests/fixtures/corpus/oss-repos/hono/src/adapter/aws-lambda/handler.ts +2 -2
  147. package/tests/fixtures/corpus/oss-repos/hono/src/adapter/cloudflare-pages/handler.ts +1 -1
  148. package/tests/fixtures/corpus/oss-repos/hono/src/adapter/cloudflare-workers/serve-static.ts +2 -2
  149. package/tests/fixtures/corpus/oss-repos/hono/src/client/client.ts +2 -2
  150. package/tests/fixtures/corpus/oss-repos/hono/src/client/types.ts +22 -20
  151. package/tests/fixtures/corpus/oss-repos/hono/src/context.ts +13 -10
  152. package/tests/fixtures/corpus/oss-repos/hono/src/helper/accepts/accepts.ts +10 -7
  153. package/tests/fixtures/corpus/oss-repos/hono/src/helper/adapter/index.ts +2 -2
  154. package/tests/fixtures/corpus/oss-repos/hono/src/helper/css/index.ts +1 -1
  155. package/tests/fixtures/corpus/oss-repos/hono/src/helper/factory/index.ts +16 -16
  156. package/tests/fixtures/corpus/oss-repos/hono/src/helper/ssg/ssg.ts +2 -2
  157. package/tests/fixtures/corpus/oss-repos/hono/src/hono-base.ts +3 -3
  158. package/tests/fixtures/corpus/oss-repos/hono/src/hono.ts +1 -1
  159. package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/css.ts +2 -2
  160. package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/intrinsic-element/components.ts +1 -1
  161. package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/render.ts +7 -7
  162. package/tests/fixtures/corpus/oss-repos/hono/src/jsx/hooks/index.ts +3 -3
  163. package/tests/fixtures/corpus/oss-repos/hono/src/jsx/intrinsic-element/components.ts +1 -1
  164. package/tests/fixtures/corpus/oss-repos/hono/src/jsx/utils.ts +6 -6
  165. package/tests/fixtures/corpus/oss-repos/hono/src/middleware/jsx-renderer/index.ts +3 -3
  166. package/tests/fixtures/corpus/oss-repos/hono/src/middleware/serve-static/index.ts +1 -1
  167. package/tests/fixtures/corpus/oss-repos/hono/src/preset/quick.ts +1 -1
  168. package/tests/fixtures/corpus/oss-repos/hono/src/preset/tiny.ts +1 -1
  169. package/tests/fixtures/corpus/oss-repos/hono/src/router/pattern-router/router.ts +2 -2
  170. package/tests/fixtures/corpus/oss-repos/hono/src/router/reg-exp-router/node.ts +4 -4
  171. package/tests/fixtures/corpus/oss-repos/hono/src/router/reg-exp-router/router.ts +1 -1
  172. package/tests/fixtures/corpus/oss-repos/hono/src/router/trie-router/node.ts +1 -1
  173. package/tests/fixtures/corpus/oss-repos/hono/src/types.ts +166 -169
  174. package/tests/fixtures/corpus/oss-repos/hono/src/utils/body.ts +8 -8
  175. package/tests/fixtures/corpus/oss-repos/hono/src/utils/color.ts +3 -3
  176. package/tests/fixtures/corpus/oss-repos/hono/src/utils/cookie.ts +2 -2
  177. package/tests/fixtures/corpus/oss-repos/hono/src/utils/encode.ts +2 -2
  178. package/tests/fixtures/corpus/oss-repos/hono/src/utils/types.ts +30 -33
  179. package/tests/fixtures/corpus/oss-repos/hono/src/validator/validator.ts +2 -2
  180. package/tests/fixtures/test-server.ts +3 -2
  181. package/tests/helpers/performance-metrics.ts +8 -25
  182. package/tests/helpers/search-relevance.ts +14 -69
  183. package/tests/integration/cli-consistency.test.ts +5 -4
  184. package/tests/integration/python-bridge.test.ts +13 -3
  185. package/tests/mcp/server.test.ts +1 -1
  186. package/tests/services/code-unit.service.test.ts +48 -0
  187. package/tests/services/job.service.test.ts +124 -0
  188. package/tests/services/search.progressive-context.test.ts +2 -2
  189. package/.claude-plugin/plugin.json +0 -13
  190. package/dist/chunk-6PBP5DVD.js.map +0 -1
  191. package/dist/chunk-L2YVNC63.js.map +0 -1
  192. package/dist/chunk-RST4XGRL.js.map +0 -1
  193. package/dist/chunk-WT2DAEO7.js.map +0 -1
  194. package/dist/watch.service-YAIKKDCF.js +0 -7
  195. package/skills/atomic-commits/SKILL.md +0 -77
  196. /package/dist/{watch.service-YAIKKDCF.js.map → watch.service-BJV3TI3F.js.map} +0 -0
@@ -17,12 +17,12 @@ vi.mock('ora', () => ({
17
17
  stop: vi.fn(),
18
18
  succeed: vi.fn(),
19
19
  fail: vi.fn(),
20
- text: ''
21
- }))
20
+ text: '',
21
+ })),
22
22
  }));
23
23
 
24
24
  vi.mock('../../services/watch.service.js', () => ({
25
- WatchService: vi.fn()
25
+ WatchService: vi.fn(),
26
26
  }));
27
27
 
28
28
  describe('createIndexCommand - Execution Tests', () => {
@@ -43,22 +43,22 @@ describe('createIndexCommand - Execution Tests', () => {
43
43
  getByIdOrName: vi.fn(),
44
44
  list: vi.fn(),
45
45
  create: vi.fn(),
46
- delete: vi.fn()
46
+ delete: vi.fn(),
47
47
  },
48
48
  lance: {
49
49
  initialize: vi.fn(),
50
50
  search: vi.fn(),
51
- addDocuments: vi.fn()
51
+ addDocuments: vi.fn(),
52
52
  },
53
53
  search: {
54
- search: vi.fn()
54
+ search: vi.fn(),
55
55
  },
56
56
  index: {
57
- indexStore: vi.fn()
57
+ indexStore: vi.fn(),
58
58
  },
59
59
  embeddings: {
60
- embed: vi.fn()
61
- }
60
+ embed: vi.fn(),
61
+ },
62
62
  } as unknown as ServiceContainer;
63
63
 
64
64
  vi.mocked(createServices).mockResolvedValue(mockServices);
@@ -74,14 +74,14 @@ describe('createIndexCommand - Execution Tests', () => {
74
74
  Object.defineProperty(process.stdout, 'isTTY', {
75
75
  value: false,
76
76
  writable: true,
77
- configurable: true
77
+ configurable: true,
78
78
  });
79
79
 
80
80
  getOptions = (): GlobalOptions => ({
81
81
  config: undefined,
82
82
  dataDir: '/tmp/test-data',
83
83
  quiet: false,
84
- format: undefined
84
+ format: undefined,
85
85
  });
86
86
  });
87
87
 
@@ -97,7 +97,7 @@ describe('createIndexCommand - Execution Tests', () => {
97
97
  type: 'file',
98
98
  path: '/test/path',
99
99
  createdAt: new Date(),
100
- updatedAt: new Date()
100
+ updatedAt: new Date(),
101
101
  };
102
102
 
103
103
  vi.mocked(mockServices.store.getByIdOrName).mockResolvedValue(mockStore);
@@ -106,8 +106,8 @@ describe('createIndexCommand - Execution Tests', () => {
106
106
  data: {
107
107
  documentsIndexed: 10,
108
108
  chunksCreated: 25,
109
- timeMs: 1500
110
- }
109
+ timeMs: 1500,
110
+ },
111
111
  });
112
112
 
113
113
  const command = createIndexCommand(getOptions);
@@ -126,7 +126,7 @@ describe('createIndexCommand - Execution Tests', () => {
126
126
  type: 'file',
127
127
  path: '/test',
128
128
  createdAt: new Date(),
129
- updatedAt: new Date()
129
+ updatedAt: new Date(),
130
130
  };
131
131
 
132
132
  vi.mocked(mockServices.store.getByIdOrName).mockResolvedValue(mockStore);
@@ -135,8 +135,8 @@ describe('createIndexCommand - Execution Tests', () => {
135
135
  data: {
136
136
  documentsIndexed: 5,
137
137
  chunksCreated: 12,
138
- timeMs: 800
139
- }
138
+ timeMs: 800,
139
+ },
140
140
  });
141
141
 
142
142
  const command = createIndexCommand(getOptions);
@@ -151,7 +151,7 @@ describe('createIndexCommand - Execution Tests', () => {
151
151
  config: undefined,
152
152
  dataDir: '/tmp/test-data',
153
153
  quiet: false,
154
- format: 'json'
154
+ format: 'json',
155
155
  });
156
156
 
157
157
  const mockStore: Store = {
@@ -160,7 +160,7 @@ describe('createIndexCommand - Execution Tests', () => {
160
160
  type: 'file',
161
161
  path: '/test',
162
162
  createdAt: new Date(),
163
- updatedAt: new Date()
163
+ updatedAt: new Date(),
164
164
  };
165
165
 
166
166
  vi.mocked(mockServices.store.getByIdOrName).mockResolvedValue(mockStore);
@@ -169,17 +169,15 @@ describe('createIndexCommand - Execution Tests', () => {
169
169
  data: {
170
170
  documentsIndexed: 3,
171
171
  chunksCreated: 7,
172
- timeMs: 500
173
- }
172
+ timeMs: 500,
173
+ },
174
174
  });
175
175
 
176
176
  const command = createIndexCommand(getOptions);
177
177
  const action = command._actionHandler;
178
178
  await action(['test']);
179
179
 
180
- expect(consoleLogSpy).toHaveBeenCalledWith(
181
- expect.stringContaining('"documentsIndexed": 3')
182
- );
180
+ expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining('"documentsIndexed": 3'));
183
181
 
184
182
  const jsonOutput = JSON.parse(consoleLogSpy.mock.calls[0]?.[0] as string);
185
183
  expect(jsonOutput.documentsIndexed).toBe(3);
@@ -192,7 +190,7 @@ describe('createIndexCommand - Execution Tests', () => {
192
190
  config: undefined,
193
191
  dataDir: '/tmp/test-data',
194
192
  quiet: true,
195
- format: undefined
193
+ format: undefined,
196
194
  });
197
195
 
198
196
  const mockStore: Store = {
@@ -201,7 +199,7 @@ describe('createIndexCommand - Execution Tests', () => {
201
199
  type: 'file',
202
200
  path: '/test',
203
201
  createdAt: new Date(),
204
- updatedAt: new Date()
202
+ updatedAt: new Date(),
205
203
  };
206
204
 
207
205
  vi.mocked(mockServices.store.getByIdOrName).mockResolvedValue(mockStore);
@@ -210,8 +208,8 @@ describe('createIndexCommand - Execution Tests', () => {
210
208
  data: {
211
209
  documentsIndexed: 1,
212
210
  chunksCreated: 2,
213
- timeMs: 100
214
- }
211
+ timeMs: 100,
212
+ },
215
213
  });
216
214
 
217
215
  const command = createIndexCommand(getOptions);
@@ -231,7 +229,7 @@ describe('createIndexCommand - Execution Tests', () => {
231
229
  type: 'file',
232
230
  path: '/test',
233
231
  createdAt: new Date(),
234
- updatedAt: new Date()
232
+ updatedAt: new Date(),
235
233
  };
236
234
 
237
235
  vi.mocked(mockServices.store.getByIdOrName).mockResolvedValue(mockStore);
@@ -240,8 +238,8 @@ describe('createIndexCommand - Execution Tests', () => {
240
238
  data: {
241
239
  documentsIndexed: 1,
242
240
  chunksCreated: 2,
243
- timeMs: 100
244
- }
241
+ timeMs: 100,
242
+ },
245
243
  });
246
244
 
247
245
  const command = createIndexCommand(getOptions);
@@ -258,7 +256,7 @@ describe('createIndexCommand - Execution Tests', () => {
258
256
  type: 'file',
259
257
  path: '/test',
260
258
  createdAt: new Date(),
261
- updatedAt: new Date()
259
+ updatedAt: new Date(),
262
260
  };
263
261
 
264
262
  let progressCallback: ((event: IndexEvent) => void) | undefined;
@@ -272,8 +270,8 @@ describe('createIndexCommand - Execution Tests', () => {
272
270
  data: {
273
271
  documentsIndexed: 5,
274
272
  chunksCreated: 10,
275
- timeMs: 1000
276
- }
273
+ timeMs: 1000,
274
+ },
277
275
  };
278
276
  });
279
277
 
@@ -291,7 +289,7 @@ describe('createIndexCommand - Execution Tests', () => {
291
289
  type: 'repo',
292
290
  url: 'https://github.com/test/repo',
293
291
  createdAt: new Date(),
294
- updatedAt: new Date()
292
+ updatedAt: new Date(),
295
293
  };
296
294
 
297
295
  vi.mocked(mockServices.store.getByIdOrName).mockResolvedValue(mockStore);
@@ -300,8 +298,8 @@ describe('createIndexCommand - Execution Tests', () => {
300
298
  data: {
301
299
  documentsIndexed: 1,
302
300
  chunksCreated: 1,
303
- timeMs: 100
304
- }
301
+ timeMs: 100,
302
+ },
305
303
  });
306
304
 
307
305
  const command = createIndexCommand(getOptions);
@@ -315,6 +313,8 @@ describe('createIndexCommand - Execution Tests', () => {
315
313
 
316
314
  describe('index command - error handling', () => {
317
315
  it('exits with code 3 when store not found', async () => {
316
+ const { destroyServices } = await import('../../services/index.js');
317
+
318
318
  vi.mocked(mockServices.store.getByIdOrName).mockResolvedValue(undefined);
319
319
 
320
320
  const command = createIndexCommand(getOptions);
@@ -323,22 +323,26 @@ describe('createIndexCommand - Execution Tests', () => {
323
323
 
324
324
  expect(consoleErrorSpy).toHaveBeenCalledWith('Error: Store not found: nonexistent-store');
325
325
  expect(processExitSpy).toHaveBeenCalledWith(3);
326
+ // Must call destroyServices before process.exit per CLAUDE.md
327
+ expect(destroyServices).toHaveBeenCalled();
326
328
  });
327
329
 
328
330
  it('exits with code 4 when indexing fails', async () => {
331
+ const { destroyServices } = await import('../../services/index.js');
332
+
329
333
  const mockStore: Store = {
330
334
  id: 'store-123',
331
335
  name: 'test',
332
336
  type: 'file',
333
337
  path: '/test',
334
338
  createdAt: new Date(),
335
- updatedAt: new Date()
339
+ updatedAt: new Date(),
336
340
  };
337
341
 
338
342
  vi.mocked(mockServices.store.getByIdOrName).mockResolvedValue(mockStore);
339
343
  vi.mocked(mockServices.index.indexStore).mockResolvedValue({
340
344
  success: false,
341
- error: new Error('Failed to read files')
345
+ error: new Error('Failed to read files'),
342
346
  });
343
347
 
344
348
  const command = createIndexCommand(getOptions);
@@ -347,6 +351,8 @@ describe('createIndexCommand - Execution Tests', () => {
347
351
 
348
352
  expect(consoleErrorSpy).toHaveBeenCalledWith('Error: Failed to read files');
349
353
  expect(processExitSpy).toHaveBeenCalledWith(4);
354
+ // Must call destroyServices before process.exit per CLAUDE.md
355
+ expect(destroyServices).toHaveBeenCalled();
350
356
  });
351
357
 
352
358
  it('handles lance initialization errors', async () => {
@@ -356,7 +362,7 @@ describe('createIndexCommand - Execution Tests', () => {
356
362
  type: 'file',
357
363
  path: '/test',
358
364
  createdAt: new Date(),
359
- updatedAt: new Date()
365
+ updatedAt: new Date(),
360
366
  };
361
367
 
362
368
  vi.mocked(mockServices.store.getByIdOrName).mockResolvedValue(mockStore);
@@ -378,21 +384,21 @@ describe('createIndexCommand - Execution Tests', () => {
378
384
  type: 'file',
379
385
  path: '/test/path',
380
386
  createdAt: new Date(),
381
- updatedAt: new Date()
387
+ updatedAt: new Date(),
382
388
  };
383
389
 
384
390
  vi.mocked(mockServices.store.getByIdOrName).mockResolvedValue(mockStore);
385
391
 
386
392
  const mockWatchService = {
387
393
  watch: vi.fn(),
388
- unwatchAll: vi.fn()
394
+ unwatchAll: vi.fn(),
389
395
  };
390
- vi.mocked(WatchService).mockImplementation(function(this: any) {
396
+ vi.mocked(WatchService).mockImplementation(function (this: any) {
391
397
  return mockWatchService as any;
392
398
  } as any);
393
399
 
394
400
  const command = createIndexCommand(getOptions);
395
- const watchCmd = command.commands.find(c => c.name() === 'watch');
401
+ const watchCmd = command.commands.find((c) => c.name() === 'watch');
396
402
  expect(watchCmd).toBeDefined();
397
403
 
398
404
  const action = watchCmd!._actionHandler;
@@ -400,11 +406,7 @@ describe('createIndexCommand - Execution Tests', () => {
400
406
 
401
407
  expect(mockServices.store.getByIdOrName).toHaveBeenCalledWith('test-store');
402
408
  expect(WatchService).toHaveBeenCalledWith(mockServices.index, mockServices.lance);
403
- expect(mockWatchService.watch).toHaveBeenCalledWith(
404
- mockStore,
405
- 1000,
406
- expect.any(Function)
407
- );
409
+ expect(mockWatchService.watch).toHaveBeenCalledWith(mockStore, 1000, expect.any(Function));
408
410
  });
409
411
 
410
412
  it('watches a repo store successfully', async () => {
@@ -416,21 +418,21 @@ describe('createIndexCommand - Execution Tests', () => {
416
418
  url: 'https://github.com/test/repo',
417
419
  path: '/local/clone/path',
418
420
  createdAt: new Date(),
419
- updatedAt: new Date()
421
+ updatedAt: new Date(),
420
422
  };
421
423
 
422
424
  vi.mocked(mockServices.store.getByIdOrName).mockResolvedValue(mockStore);
423
425
 
424
426
  const mockWatchService = {
425
427
  watch: vi.fn(),
426
- unwatchAll: vi.fn()
428
+ unwatchAll: vi.fn(),
427
429
  };
428
- vi.mocked(WatchService).mockImplementation(function(this: any) {
430
+ vi.mocked(WatchService).mockImplementation(function (this: any) {
429
431
  return mockWatchService as any;
430
432
  } as any);
431
433
 
432
434
  const command = createIndexCommand(getOptions);
433
- const watchCmd = command.commands.find(c => c.name() === 'watch');
435
+ const watchCmd = command.commands.find((c) => c.name() === 'watch');
434
436
  const action = watchCmd!._actionHandler;
435
437
  await action(['repo-store']);
436
438
 
@@ -445,30 +447,26 @@ describe('createIndexCommand - Execution Tests', () => {
445
447
  type: 'file',
446
448
  path: '/test',
447
449
  createdAt: new Date(),
448
- updatedAt: new Date()
450
+ updatedAt: new Date(),
449
451
  };
450
452
 
451
453
  vi.mocked(mockServices.store.getByIdOrName).mockResolvedValue(mockStore);
452
454
 
453
455
  const mockWatchService = {
454
456
  watch: vi.fn(),
455
- unwatchAll: vi.fn()
457
+ unwatchAll: vi.fn(),
456
458
  };
457
- vi.mocked(WatchService).mockImplementation(function(this: any) {
459
+ vi.mocked(WatchService).mockImplementation(function (this: any) {
458
460
  return mockWatchService as any;
459
461
  } as any);
460
462
 
461
463
  const command = createIndexCommand(getOptions);
462
- const watchCmd = command.commands.find(c => c.name() === 'watch');
464
+ const watchCmd = command.commands.find((c) => c.name() === 'watch');
463
465
  watchCmd.parseOptions(['--debounce', '2500']);
464
466
  const action = watchCmd!._actionHandler;
465
467
  await action(['test']);
466
468
 
467
- expect(mockWatchService.watch).toHaveBeenCalledWith(
468
- mockStore,
469
- 2500,
470
- expect.any(Function)
471
- );
469
+ expect(mockWatchService.watch).toHaveBeenCalledWith(mockStore, 2500, expect.any(Function));
472
470
  });
473
471
 
474
472
  it('outputs watching message in normal mode', async () => {
@@ -479,21 +477,21 @@ describe('createIndexCommand - Execution Tests', () => {
479
477
  type: 'file',
480
478
  path: '/test',
481
479
  createdAt: new Date(),
482
- updatedAt: new Date()
480
+ updatedAt: new Date(),
483
481
  };
484
482
 
485
483
  vi.mocked(mockServices.store.getByIdOrName).mockResolvedValue(mockStore);
486
484
 
487
485
  const mockWatchService = {
488
486
  watch: vi.fn(),
489
- unwatchAll: vi.fn()
487
+ unwatchAll: vi.fn(),
490
488
  };
491
- vi.mocked(WatchService).mockImplementation(function(this: any) {
489
+ vi.mocked(WatchService).mockImplementation(function (this: any) {
492
490
  return mockWatchService as any;
493
491
  } as any);
494
492
 
495
493
  const command = createIndexCommand(getOptions);
496
- const watchCmd = command.commands.find(c => c.name() === 'watch');
494
+ const watchCmd = command.commands.find((c) => c.name() === 'watch');
497
495
  const action = watchCmd!._actionHandler;
498
496
  await action(['my-store']);
499
497
 
@@ -505,7 +503,7 @@ describe('createIndexCommand - Execution Tests', () => {
505
503
  config: undefined,
506
504
  dataDir: '/tmp/test-data',
507
505
  quiet: true,
508
- format: undefined
506
+ format: undefined,
509
507
  });
510
508
 
511
509
  const { WatchService } = await import('../../services/watch.service.js');
@@ -515,21 +513,21 @@ describe('createIndexCommand - Execution Tests', () => {
515
513
  type: 'file',
516
514
  path: '/test',
517
515
  createdAt: new Date(),
518
- updatedAt: new Date()
516
+ updatedAt: new Date(),
519
517
  };
520
518
 
521
519
  vi.mocked(mockServices.store.getByIdOrName).mockResolvedValue(mockStore);
522
520
 
523
521
  const mockWatchService = {
524
522
  watch: vi.fn(),
525
- unwatchAll: vi.fn()
523
+ unwatchAll: vi.fn(),
526
524
  };
527
- vi.mocked(WatchService).mockImplementation(function(this: any) {
525
+ vi.mocked(WatchService).mockImplementation(function (this: any) {
528
526
  return mockWatchService as any;
529
527
  } as any);
530
528
 
531
529
  const command = createIndexCommand(getOptions);
532
- const watchCmd = command.commands.find(c => c.name() === 'watch');
530
+ const watchCmd = command.commands.find((c) => c.name() === 'watch');
533
531
  const action = watchCmd!._actionHandler;
534
532
  await action(['test']);
535
533
 
@@ -544,7 +542,7 @@ describe('createIndexCommand - Execution Tests', () => {
544
542
  type: 'file',
545
543
  path: '/test',
546
544
  createdAt: new Date(),
547
- updatedAt: new Date()
545
+ updatedAt: new Date(),
548
546
  };
549
547
 
550
548
  vi.mocked(mockServices.store.getByIdOrName).mockResolvedValue(mockStore);
@@ -554,14 +552,14 @@ describe('createIndexCommand - Execution Tests', () => {
554
552
  watch: vi.fn((store, debounce, callback) => {
555
553
  capturedCallback = callback;
556
554
  }),
557
- unwatchAll: vi.fn()
555
+ unwatchAll: vi.fn(),
558
556
  };
559
- vi.mocked(WatchService).mockImplementation(function(this: any) {
557
+ vi.mocked(WatchService).mockImplementation(function (this: any) {
560
558
  return mockWatchService as any;
561
559
  } as any);
562
560
 
563
561
  const command = createIndexCommand(getOptions);
564
- const watchCmd = command.commands.find(c => c.name() === 'watch');
562
+ const watchCmd = command.commands.find((c) => c.name() === 'watch');
565
563
  const action = watchCmd!._actionHandler;
566
564
  await action(['test']);
567
565
 
@@ -579,28 +577,30 @@ describe('createIndexCommand - Execution Tests', () => {
579
577
  type: 'file',
580
578
  path: '/test',
581
579
  createdAt: new Date(),
582
- updatedAt: new Date()
580
+ updatedAt: new Date(),
583
581
  };
584
582
 
585
583
  vi.mocked(mockServices.store.getByIdOrName).mockResolvedValue(mockStore);
586
584
 
587
585
  const mockWatchService = {
588
586
  watch: vi.fn(),
589
- unwatchAll: vi.fn()
587
+ unwatchAll: vi.fn(),
590
588
  };
591
- vi.mocked(WatchService).mockImplementation(function(this: any) {
589
+ vi.mocked(WatchService).mockImplementation(function (this: any) {
592
590
  return mockWatchService as any;
593
591
  } as any);
594
592
 
595
593
  const command = createIndexCommand(getOptions);
596
- const watchCmd = command.commands.find(c => c.name() === 'watch');
594
+ const watchCmd = command.commands.find((c) => c.name() === 'watch');
597
595
  const action = watchCmd!._actionHandler;
598
596
  await action(['test']);
599
597
 
600
598
  expect(processOnSpy).toHaveBeenCalledWith('SIGINT', expect.any(Function));
601
599
 
602
600
  // Get the SIGINT handler and call it
603
- const sigintHandler = processOnSpy.mock.calls.find(call => call[0] === 'SIGINT')?.[1] as () => void;
601
+ const sigintHandler = processOnSpy.mock.calls.find(
602
+ (call) => call[0] === 'SIGINT'
603
+ )?.[1] as () => void;
604
604
  expect(sigintHandler).toBeDefined();
605
605
 
606
606
  // Call the handler
@@ -616,7 +616,7 @@ describe('createIndexCommand - Execution Tests', () => {
616
616
  vi.mocked(mockServices.store.getByIdOrName).mockResolvedValue(undefined);
617
617
 
618
618
  const command = createIndexCommand(getOptions);
619
- const watchCmd = command.commands.find(c => c.name() === 'watch');
619
+ const watchCmd = command.commands.find((c) => c.name() === 'watch');
620
620
  const action = watchCmd!._actionHandler;
621
621
  await expect(action(['nonexistent'])).rejects.toThrow('process.exit: 3');
622
622
 
@@ -631,13 +631,13 @@ describe('createIndexCommand - Execution Tests', () => {
631
631
  type: 'web',
632
632
  url: 'https://example.com',
633
633
  createdAt: new Date(),
634
- updatedAt: new Date()
634
+ updatedAt: new Date(),
635
635
  };
636
636
 
637
637
  vi.mocked(mockServices.store.getByIdOrName).mockResolvedValue(mockStore);
638
638
 
639
639
  const command = createIndexCommand(getOptions);
640
- const watchCmd = command.commands.find(c => c.name() === 'watch');
640
+ const watchCmd = command.commands.find((c) => c.name() === 'watch');
641
641
  const action = watchCmd!._actionHandler;
642
642
  await expect(action(['web-store'])).rejects.toThrow('process.exit: 3');
643
643
 
@@ -653,7 +653,7 @@ describe('createIndexCommand - Execution Tests', () => {
653
653
  config: '/custom/config.json',
654
654
  dataDir: '/custom/data',
655
655
  quiet: false,
656
- format: undefined
656
+ format: undefined,
657
657
  });
658
658
 
659
659
  const mockStore: Store = {
@@ -662,7 +662,7 @@ describe('createIndexCommand - Execution Tests', () => {
662
662
  type: 'file',
663
663
  path: '/test',
664
664
  createdAt: new Date(),
665
- updatedAt: new Date()
665
+ updatedAt: new Date(),
666
666
  };
667
667
 
668
668
  vi.mocked(mockServices.store.getByIdOrName).mockResolvedValue(mockStore);
@@ -671,8 +671,8 @@ describe('createIndexCommand - Execution Tests', () => {
671
671
  data: {
672
672
  documentsIndexed: 1,
673
673
  chunksCreated: 1,
674
- timeMs: 100
675
- }
674
+ timeMs: 100,
675
+ },
676
676
  });
677
677
 
678
678
  const command = createIndexCommand(getOptions);
@@ -690,7 +690,7 @@ describe('createIndexCommand - Execution Tests', () => {
690
690
  type: 'file',
691
691
  path: '/test',
692
692
  createdAt: new Date(),
693
- updatedAt: new Date()
693
+ updatedAt: new Date(),
694
694
  };
695
695
 
696
696
  vi.mocked(mockServices.store.getByIdOrName).mockImplementation(async () => {
@@ -707,8 +707,8 @@ describe('createIndexCommand - Execution Tests', () => {
707
707
  data: {
708
708
  documentsIndexed: 1,
709
709
  chunksCreated: 1,
710
- timeMs: 100
711
- }
710
+ timeMs: 100,
711
+ },
712
712
  };
713
713
  });
714
714
 
@@ -11,63 +11,79 @@ export function createIndexCommand(getOptions: () => GlobalOptions): Command {
11
11
  .action(async (storeIdOrName: string, _options: { force?: boolean }) => {
12
12
  const globalOpts = getOptions();
13
13
  const services = await createServices(globalOpts.config, globalOpts.dataDir);
14
+ let exitCode = 0;
14
15
  try {
15
- const store = await services.store.getByIdOrName(storeIdOrName);
16
+ indexLogic: {
17
+ const store = await services.store.getByIdOrName(storeIdOrName);
16
18
 
17
- if (store === undefined) {
18
- console.error(`Error: Store not found: ${storeIdOrName}`);
19
- process.exit(3);
20
- }
19
+ if (store === undefined) {
20
+ console.error(`Error: Store not found: ${storeIdOrName}`);
21
+ exitCode = 3;
21
22
 
22
- // Use spinner in interactive mode (not quiet, not json output)
23
- const isInteractive = process.stdout.isTTY && globalOpts.quiet !== true && globalOpts.format !== 'json';
24
- let spinner: Ora | undefined;
23
+ break indexLogic;
24
+ }
25
25
 
26
- if (isInteractive) {
27
- spinner = ora(`Indexing store: ${store.name}`).start();
28
- } else if (globalOpts.quiet !== true && globalOpts.format !== 'json') {
29
- console.log(`Indexing store: ${store.name}`);
30
- }
26
+ // Use spinner in interactive mode (not quiet, not json output)
27
+ const isInteractive =
28
+ process.stdout.isTTY && globalOpts.quiet !== true && globalOpts.format !== 'json';
29
+ let spinner: Ora | undefined;
31
30
 
32
- await services.lance.initialize(store.id);
31
+ if (isInteractive) {
32
+ spinner = ora(`Indexing store: ${store.name}`).start();
33
+ } else if (globalOpts.quiet !== true && globalOpts.format !== 'json') {
34
+ console.log(`Indexing store: ${store.name}`);
35
+ }
33
36
 
34
- const result = await services.index.indexStore(store, (event) => {
35
- if (event.type === 'progress') {
36
- if (spinner) {
37
- spinner.text = `Indexing: ${String(event.current)}/${String(event.total)} files - ${event.message}`;
37
+ await services.lance.initialize(store.id);
38
+
39
+ const result = await services.index.indexStore(store, (event) => {
40
+ if (event.type === 'progress') {
41
+ if (spinner) {
42
+ spinner.text = `Indexing: ${String(event.current)}/${String(event.total)} files - ${event.message}`;
43
+ }
38
44
  }
39
- }
40
- });
45
+ });
41
46
 
42
- if (result.success) {
43
- if (globalOpts.format === 'json') {
44
- console.log(JSON.stringify(result.data, null, 2));
47
+ if (result.success) {
48
+ if (globalOpts.format === 'json') {
49
+ console.log(JSON.stringify(result.data, null, 2));
50
+ } else {
51
+ const message = `Indexed ${String(result.data.documentsIndexed)} documents, ${String(result.data.chunksCreated)} chunks in ${String(result.data.timeMs)}ms`;
52
+ if (spinner !== undefined) {
53
+ spinner.succeed(message);
54
+ } else if (globalOpts.quiet !== true) {
55
+ console.log(message);
56
+ }
57
+ }
45
58
  } else {
46
- const message = `Indexed ${String(result.data.documentsIndexed)} documents, ${String(result.data.chunksCreated)} chunks in ${String(result.data.timeMs)}ms`;
59
+ const message = `Error: ${result.error.message}`;
47
60
  if (spinner !== undefined) {
48
- spinner.succeed(message);
49
- } else if (globalOpts.quiet !== true) {
50
- console.log(message);
61
+ spinner.fail(message);
62
+ } else {
63
+ console.error(message);
51
64
  }
65
+ exitCode = 4;
66
+
67
+ break indexLogic;
52
68
  }
53
- } else {
54
- const message = `Error: ${result.error.message}`;
55
- if (spinner !== undefined) {
56
- spinner.fail(message);
57
- } else {
58
- console.error(message);
59
- }
60
- process.exit(4);
61
69
  }
62
70
  } finally {
63
71
  await destroyServices(services);
64
72
  }
73
+
74
+ if (exitCode !== 0) {
75
+ process.exit(exitCode);
76
+ }
65
77
  });
66
78
 
67
79
  index
68
80
  .command('watch <store>')
69
81
  .description('Watch store directory; re-index when files change')
70
- .option('--debounce <ms>', 'Wait N ms after last change before re-indexing (default: 1000)', '1000')
82
+ .option(
83
+ '--debounce <ms>',
84
+ 'Wait N ms after last change before re-indexing (default: 1000)',
85
+ '1000'
86
+ )
71
87
  .action(async (storeIdOrName: string, options: { debounce?: string }) => {
72
88
  const globalOpts = getOptions();
73
89
  const services = await createServices(globalOpts.config, globalOpts.dataDir);