@skillsmith/core 0.4.13 → 0.4.15

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 (296) hide show
  1. package/README.md +2 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/src/analytics/storage.d.ts +8 -2
  4. package/dist/src/analytics/storage.d.ts.map +1 -1
  5. package/dist/src/analytics/storage.js +16 -5
  6. package/dist/src/analytics/storage.js.map +1 -1
  7. package/dist/src/analytics/usage-tracker.d.ts +8 -2
  8. package/dist/src/analytics/usage-tracker.d.ts.map +1 -1
  9. package/dist/src/analytics/usage-tracker.js +24 -11
  10. package/dist/src/analytics/usage-tracker.js.map +1 -1
  11. package/dist/src/api/client.d.ts +1 -0
  12. package/dist/src/api/client.d.ts.map +1 -1
  13. package/dist/src/api/client.health.d.ts +26 -0
  14. package/dist/src/api/client.health.d.ts.map +1 -0
  15. package/dist/src/api/client.health.js +74 -0
  16. package/dist/src/api/client.health.js.map +1 -0
  17. package/dist/src/api/client.js +3 -51
  18. package/dist/src/api/client.js.map +1 -1
  19. package/dist/src/benchmarks/IndexBenchmark.js +2 -2
  20. package/dist/src/benchmarks/IndexBenchmark.js.map +1 -1
  21. package/dist/src/benchmarks/SearchBenchmark.js +2 -2
  22. package/dist/src/benchmarks/SearchBenchmark.js.map +1 -1
  23. package/dist/src/benchmarks/cacheBenchmark.js +2 -2
  24. package/dist/src/benchmarks/cacheBenchmark.js.map +1 -1
  25. package/dist/src/benchmarks/embeddingBenchmark.js +1 -1
  26. package/dist/src/benchmarks/embeddingBenchmark.js.map +1 -1
  27. package/dist/src/cache/CacheManager.d.ts +13 -2
  28. package/dist/src/cache/CacheManager.d.ts.map +1 -1
  29. package/dist/src/cache/CacheManager.js +27 -6
  30. package/dist/src/cache/CacheManager.js.map +1 -1
  31. package/dist/src/cache/TieredCache.d.ts +15 -2
  32. package/dist/src/cache/TieredCache.d.ts.map +1 -1
  33. package/dist/src/cache/TieredCache.js +52 -70
  34. package/dist/src/cache/TieredCache.js.map +1 -1
  35. package/dist/src/cache/index.d.ts +12 -0
  36. package/dist/src/cache/index.d.ts.map +1 -1
  37. package/dist/src/cache/index.js +20 -1
  38. package/dist/src/cache/index.js.map +1 -1
  39. package/dist/src/cache/sqlite.d.ts +15 -1
  40. package/dist/src/cache/sqlite.d.ts.map +1 -1
  41. package/dist/src/cache/sqlite.js +22 -4
  42. package/dist/src/cache/sqlite.js.map +1 -1
  43. package/dist/src/db/createDatabase.d.ts +9 -1
  44. package/dist/src/db/createDatabase.d.ts.map +1 -1
  45. package/dist/src/db/createDatabase.js +21 -1
  46. package/dist/src/db/createDatabase.js.map +1 -1
  47. package/dist/src/db/drivers/sqljsDriver.d.ts.map +1 -1
  48. package/dist/src/db/drivers/sqljsDriver.js +5 -2
  49. package/dist/src/db/drivers/sqljsDriver.js.map +1 -1
  50. package/dist/src/db/migrations/v5-skill-versions.d.ts +30 -0
  51. package/dist/src/db/migrations/v5-skill-versions.d.ts.map +1 -0
  52. package/dist/src/db/migrations/v5-skill-versions.js +45 -0
  53. package/dist/src/db/migrations/v5-skill-versions.js.map +1 -0
  54. package/dist/src/db/migrations/v5b-change-type.d.ts +19 -0
  55. package/dist/src/db/migrations/v5b-change-type.d.ts.map +1 -0
  56. package/dist/src/db/migrations/v5b-change-type.js +22 -0
  57. package/dist/src/db/migrations/v5b-change-type.js.map +1 -0
  58. package/dist/src/db/migrations/v6-advisories.d.ts +32 -0
  59. package/dist/src/db/migrations/v6-advisories.d.ts.map +1 -0
  60. package/dist/src/db/migrations/v6-advisories.js +53 -0
  61. package/dist/src/db/migrations/v6-advisories.js.map +1 -0
  62. package/dist/src/db/migrations/v7-compatibility.d.ts +21 -0
  63. package/dist/src/db/migrations/v7-compatibility.d.ts.map +1 -0
  64. package/dist/src/db/migrations/v7-compatibility.js +23 -0
  65. package/dist/src/db/migrations/v7-compatibility.js.map +1 -0
  66. package/dist/src/db/migrations/v8-co-installs.d.ts +21 -0
  67. package/dist/src/db/migrations/v8-co-installs.d.ts.map +1 -0
  68. package/dist/src/db/migrations/v8-co-installs.js +33 -0
  69. package/dist/src/db/migrations/v8-co-installs.js.map +1 -0
  70. package/dist/src/db/schema.d.ts +6 -11
  71. package/dist/src/db/schema.d.ts.map +1 -1
  72. package/dist/src/db/schema.js +38 -17
  73. package/dist/src/db/schema.js.map +1 -1
  74. package/dist/src/embeddings/hnsw-store.d.ts +14 -0
  75. package/dist/src/embeddings/hnsw-store.d.ts.map +1 -1
  76. package/dist/src/embeddings/hnsw-store.helpers.d.ts +4 -4
  77. package/dist/src/embeddings/hnsw-store.helpers.d.ts.map +1 -1
  78. package/dist/src/embeddings/hnsw-store.helpers.js +5 -5
  79. package/dist/src/embeddings/hnsw-store.helpers.js.map +1 -1
  80. package/dist/src/embeddings/hnsw-store.js +32 -4
  81. package/dist/src/embeddings/hnsw-store.js.map +1 -1
  82. package/dist/src/embeddings/index.d.ts +15 -1
  83. package/dist/src/embeddings/index.d.ts.map +1 -1
  84. package/dist/src/embeddings/index.js +24 -4
  85. package/dist/src/embeddings/index.js.map +1 -1
  86. package/dist/src/exports/repositories.d.ts +5 -0
  87. package/dist/src/exports/repositories.d.ts.map +1 -1
  88. package/dist/src/exports/repositories.js +17 -0
  89. package/dist/src/exports/repositories.js.map +1 -1
  90. package/dist/src/exports/types.d.ts +1 -1
  91. package/dist/src/exports/types.d.ts.map +1 -1
  92. package/dist/src/exports/types.js.map +1 -1
  93. package/dist/src/index.d.ts +1 -1
  94. package/dist/src/index.d.ts.map +1 -1
  95. package/dist/src/index.js +2 -1
  96. package/dist/src/index.js.map +1 -1
  97. package/dist/src/indexer/SkillParser.d.ts +2 -0
  98. package/dist/src/indexer/SkillParser.d.ts.map +1 -1
  99. package/dist/src/indexer/SkillParser.js.map +1 -1
  100. package/dist/src/learning/PatternStore.d.ts +8 -2
  101. package/dist/src/learning/PatternStore.d.ts.map +1 -1
  102. package/dist/src/learning/PatternStore.helpers.d.ts +19 -1
  103. package/dist/src/learning/PatternStore.helpers.d.ts.map +1 -1
  104. package/dist/src/learning/PatternStore.helpers.js +51 -0
  105. package/dist/src/learning/PatternStore.helpers.js.map +1 -1
  106. package/dist/src/learning/PatternStore.js +19 -39
  107. package/dist/src/learning/PatternStore.js.map +1 -1
  108. package/dist/src/repositories/AdvisoryRepository.d.ts +80 -0
  109. package/dist/src/repositories/AdvisoryRepository.d.ts.map +1 -0
  110. package/dist/src/repositories/AdvisoryRepository.js +128 -0
  111. package/dist/src/repositories/AdvisoryRepository.js.map +1 -0
  112. package/dist/src/repositories/AdvisoryRepository.test.d.ts +6 -0
  113. package/dist/src/repositories/AdvisoryRepository.test.d.ts.map +1 -0
  114. package/dist/src/repositories/AdvisoryRepository.test.js +149 -0
  115. package/dist/src/repositories/AdvisoryRepository.test.js.map +1 -0
  116. package/dist/src/repositories/CoInstallRepository.d.ts +68 -0
  117. package/dist/src/repositories/CoInstallRepository.d.ts.map +1 -0
  118. package/dist/src/repositories/CoInstallRepository.js +122 -0
  119. package/dist/src/repositories/CoInstallRepository.js.map +1 -0
  120. package/dist/src/repositories/SkillRepository.d.ts.map +1 -1
  121. package/dist/src/repositories/SkillRepository.js +16 -5
  122. package/dist/src/repositories/SkillRepository.js.map +1 -1
  123. package/dist/src/repositories/SkillVersionRepository.d.ts +82 -0
  124. package/dist/src/repositories/SkillVersionRepository.d.ts.map +1 -0
  125. package/dist/src/repositories/SkillVersionRepository.js +118 -0
  126. package/dist/src/repositories/SkillVersionRepository.js.map +1 -0
  127. package/dist/src/search/hybrid.d.ts +15 -4
  128. package/dist/src/search/hybrid.d.ts.map +1 -1
  129. package/dist/src/search/hybrid.js +25 -9
  130. package/dist/src/search/hybrid.js.map +1 -1
  131. package/dist/src/services/SearchService.helpers.d.ts.map +1 -1
  132. package/dist/src/services/SearchService.helpers.js +4 -0
  133. package/dist/src/services/SearchService.helpers.js.map +1 -1
  134. package/dist/src/services/SearchService.types.d.ts +1 -0
  135. package/dist/src/services/SearchService.types.d.ts.map +1 -1
  136. package/dist/src/services/TaskRunner.d.ts +5 -81
  137. package/dist/src/services/TaskRunner.d.ts.map +1 -1
  138. package/dist/src/services/TaskRunner.js +7 -68
  139. package/dist/src/services/TaskRunner.js.map +1 -1
  140. package/dist/src/services/TaskRunner.process.d.ts +33 -0
  141. package/dist/src/services/TaskRunner.process.d.ts.map +1 -0
  142. package/dist/src/services/TaskRunner.process.js +70 -0
  143. package/dist/src/services/TaskRunner.process.js.map +1 -0
  144. package/dist/src/services/TaskRunner.types.d.ts +76 -0
  145. package/dist/src/services/TaskRunner.types.d.ts.map +1 -0
  146. package/dist/src/services/TaskRunner.types.js +22 -0
  147. package/dist/src/services/TaskRunner.types.js.map +1 -0
  148. package/dist/src/services/__tests__/TaskRunner.process.test.d.ts +13 -0
  149. package/dist/src/services/__tests__/TaskRunner.process.test.d.ts.map +1 -0
  150. package/dist/src/services/__tests__/TaskRunner.process.test.js +91 -0
  151. package/dist/src/services/__tests__/TaskRunner.process.test.js.map +1 -0
  152. package/dist/src/services/quarantine/QuarantineService.d.ts +3 -24
  153. package/dist/src/services/quarantine/QuarantineService.d.ts.map +1 -1
  154. package/dist/src/services/quarantine/QuarantineService.js +8 -205
  155. package/dist/src/services/quarantine/QuarantineService.js.map +1 -1
  156. package/dist/src/services/quarantine/QuarantineService.multiapproval.d.ts +57 -0
  157. package/dist/src/services/quarantine/QuarantineService.multiapproval.d.ts.map +1 -0
  158. package/dist/src/services/quarantine/QuarantineService.multiapproval.js +211 -0
  159. package/dist/src/services/quarantine/QuarantineService.multiapproval.js.map +1 -0
  160. package/dist/src/session/SessionManager.d.ts +2 -33
  161. package/dist/src/session/SessionManager.d.ts.map +1 -1
  162. package/dist/src/session/SessionManager.js +14 -238
  163. package/dist/src/session/SessionManager.js.map +1 -1
  164. package/dist/src/session/SessionManager.memory.d.ts +67 -0
  165. package/dist/src/session/SessionManager.memory.d.ts.map +1 -0
  166. package/dist/src/session/SessionManager.memory.js +262 -0
  167. package/dist/src/session/SessionManager.memory.js.map +1 -0
  168. package/dist/src/sync/SyncEngine.d.ts +13 -1
  169. package/dist/src/sync/SyncEngine.d.ts.map +1 -1
  170. package/dist/src/sync/SyncEngine.js +50 -1
  171. package/dist/src/sync/SyncEngine.js.map +1 -1
  172. package/dist/src/testing/MultiLLMProvider.d.ts +5 -6
  173. package/dist/src/testing/MultiLLMProvider.d.ts.map +1 -1
  174. package/dist/src/testing/MultiLLMProvider.js +18 -130
  175. package/dist/src/testing/MultiLLMProvider.js.map +1 -1
  176. package/dist/src/testing/MultiLLMProvider.metrics.d.ts +33 -0
  177. package/dist/src/testing/MultiLLMProvider.metrics.d.ts.map +1 -0
  178. package/dist/src/testing/MultiLLMProvider.metrics.js +87 -0
  179. package/dist/src/testing/MultiLLMProvider.metrics.js.map +1 -0
  180. package/dist/src/testing/MultiLLMProvider.selection.d.ts +77 -0
  181. package/dist/src/testing/MultiLLMProvider.selection.d.ts.map +1 -0
  182. package/dist/src/testing/MultiLLMProvider.selection.js +151 -0
  183. package/dist/src/testing/MultiLLMProvider.selection.js.map +1 -0
  184. package/dist/src/types/skill.d.ts +10 -0
  185. package/dist/src/types/skill.d.ts.map +1 -1
  186. package/dist/src/types.d.ts +34 -0
  187. package/dist/src/types.d.ts.map +1 -1
  188. package/dist/src/versioning/change-classifier.d.ts +38 -0
  189. package/dist/src/versioning/change-classifier.d.ts.map +1 -0
  190. package/dist/src/versioning/change-classifier.js +187 -0
  191. package/dist/src/versioning/change-classifier.js.map +1 -0
  192. package/dist/src/versioning/change-classifier.test.d.ts +6 -0
  193. package/dist/src/versioning/change-classifier.test.d.ts.map +1 -0
  194. package/dist/src/versioning/change-classifier.test.js +275 -0
  195. package/dist/src/versioning/change-classifier.test.js.map +1 -0
  196. package/dist/src/versioning/update-risk.d.ts +50 -0
  197. package/dist/src/versioning/update-risk.d.ts.map +1 -0
  198. package/dist/src/versioning/update-risk.js +80 -0
  199. package/dist/src/versioning/update-risk.js.map +1 -0
  200. package/dist/src/versioning/update-risk.test.d.ts +6 -0
  201. package/dist/src/versioning/update-risk.test.d.ts.map +1 -0
  202. package/dist/src/versioning/update-risk.test.js +200 -0
  203. package/dist/src/versioning/update-risk.test.js.map +1 -0
  204. package/dist/tests/AnalyticsStorage.test.js +2 -2
  205. package/dist/tests/AnalyticsStorage.test.js.map +1 -1
  206. package/dist/tests/AuditLogger.edge-cases.test.d.ts +10 -0
  207. package/dist/tests/AuditLogger.edge-cases.test.d.ts.map +1 -0
  208. package/dist/tests/AuditLogger.edge-cases.test.js +183 -0
  209. package/dist/tests/AuditLogger.edge-cases.test.js.map +1 -0
  210. package/dist/tests/CacheManager.test.d.ts +9 -0
  211. package/dist/tests/CacheManager.test.d.ts.map +1 -0
  212. package/dist/tests/CacheManager.test.js +163 -0
  213. package/dist/tests/CacheManager.test.js.map +1 -0
  214. package/dist/tests/CacheSecurity.test.js +4 -4
  215. package/dist/tests/CacheSecurity.test.js.map +1 -1
  216. package/dist/tests/EmbeddingService.test.js +11 -12
  217. package/dist/tests/EmbeddingService.test.js.map +1 -1
  218. package/dist/tests/GitHubIndexer.edge-cases.test.d.ts +10 -0
  219. package/dist/tests/GitHubIndexer.edge-cases.test.d.ts.map +1 -0
  220. package/dist/tests/GitHubIndexer.edge-cases.test.js +255 -0
  221. package/dist/tests/GitHubIndexer.edge-cases.test.js.map +1 -0
  222. package/dist/tests/HybridSearch.test.d.ts +8 -0
  223. package/dist/tests/HybridSearch.test.d.ts.map +1 -0
  224. package/dist/tests/HybridSearch.test.js +48 -0
  225. package/dist/tests/HybridSearch.test.js.map +1 -0
  226. package/dist/tests/SearchService.test.js +71 -0
  227. package/dist/tests/SearchService.test.js.map +1 -1
  228. package/dist/tests/SkillVersionRepository.test.d.ts +2 -0
  229. package/dist/tests/SkillVersionRepository.test.d.ts.map +1 -0
  230. package/dist/tests/SkillVersionRepository.test.js +168 -0
  231. package/dist/tests/SkillVersionRepository.test.js.map +1 -0
  232. package/dist/tests/TieredCache.test.js +10 -10
  233. package/dist/tests/TieredCache.test.js.map +1 -1
  234. package/dist/tests/UsageTracker.test.js +6 -6
  235. package/dist/tests/UsageTracker.test.js.map +1 -1
  236. package/dist/tests/analytics/usage-tracker.test.js +8 -8
  237. package/dist/tests/analytics/usage-tracker.test.js.map +1 -1
  238. package/dist/tests/api/client.health.test.d.ts +15 -0
  239. package/dist/tests/api/client.health.test.d.ts.map +1 -0
  240. package/dist/tests/api/client.health.test.js +111 -0
  241. package/dist/tests/api/client.health.test.js.map +1 -0
  242. package/dist/tests/cache.test.js +7 -9
  243. package/dist/tests/cache.test.js.map +1 -1
  244. package/dist/tests/db/betterSqlite3Driver.test.d.ts +8 -0
  245. package/dist/tests/db/betterSqlite3Driver.test.d.ts.map +1 -0
  246. package/dist/tests/db/betterSqlite3Driver.test.js +88 -0
  247. package/dist/tests/db/betterSqlite3Driver.test.js.map +1 -0
  248. package/dist/tests/db/database-abstraction.test.js +48 -0
  249. package/dist/tests/db/database-abstraction.test.js.map +1 -1
  250. package/dist/tests/db/schema-migrations.test.d.ts +10 -0
  251. package/dist/tests/db/schema-migrations.test.d.ts.map +1 -0
  252. package/dist/tests/db/schema-migrations.test.js +134 -0
  253. package/dist/tests/db/schema-migrations.test.js.map +1 -0
  254. package/dist/tests/db/sqljsDriver.test.d.ts +9 -0
  255. package/dist/tests/db/sqljsDriver.test.d.ts.map +1 -0
  256. package/dist/tests/db/sqljsDriver.test.js +75 -0
  257. package/dist/tests/db/sqljsDriver.test.js.map +1 -0
  258. package/dist/tests/embeddings/hnsw-store.test.js +27 -24
  259. package/dist/tests/embeddings/hnsw-store.test.js.map +1 -1
  260. package/dist/tests/helpers/database.d.ts +32 -0
  261. package/dist/tests/helpers/database.d.ts.map +1 -0
  262. package/dist/tests/helpers/database.js +54 -0
  263. package/dist/tests/helpers/database.js.map +1 -0
  264. package/dist/tests/learning/PatternStore.helpers.test.d.ts +17 -0
  265. package/dist/tests/learning/PatternStore.helpers.test.d.ts.map +1 -0
  266. package/dist/tests/learning/PatternStore.helpers.test.js +301 -0
  267. package/dist/tests/learning/PatternStore.helpers.test.js.map +1 -0
  268. package/dist/tests/learning/PatternStore.test.js +9 -0
  269. package/dist/tests/learning/PatternStore.test.js.map +1 -1
  270. package/dist/tests/repositories/CoInstallRepository.test.d.ts +9 -0
  271. package/dist/tests/repositories/CoInstallRepository.test.d.ts.map +1 -0
  272. package/dist/tests/repositories/CoInstallRepository.test.js +126 -0
  273. package/dist/tests/repositories/CoInstallRepository.test.js.map +1 -0
  274. package/dist/tests/routing/LanguageRouter.test.d.ts +9 -0
  275. package/dist/tests/routing/LanguageRouter.test.d.ts.map +1 -0
  276. package/dist/tests/routing/LanguageRouter.test.js +150 -0
  277. package/dist/tests/routing/LanguageRouter.test.js.map +1 -0
  278. package/dist/tests/session/SessionManager.memory.test.d.ts +11 -0
  279. package/dist/tests/session/SessionManager.memory.test.d.ts.map +1 -0
  280. package/dist/tests/session/SessionManager.memory.test.js +219 -0
  281. package/dist/tests/session/SessionManager.memory.test.js.map +1 -0
  282. package/dist/tests/sync/SyncEngine.test.js +32 -18
  283. package/dist/tests/sync/SyncEngine.test.js.map +1 -1
  284. package/dist/tests/testing/MultiLLMProvider.metrics.test.d.ts +13 -0
  285. package/dist/tests/testing/MultiLLMProvider.metrics.test.d.ts.map +1 -0
  286. package/dist/tests/testing/MultiLLMProvider.metrics.test.js +149 -0
  287. package/dist/tests/testing/MultiLLMProvider.metrics.test.js.map +1 -0
  288. package/dist/tests/testing/MultiLLMProvider.selection.test.d.ts +15 -0
  289. package/dist/tests/testing/MultiLLMProvider.selection.test.d.ts.map +1 -0
  290. package/dist/tests/testing/MultiLLMProvider.selection.test.js +249 -0
  291. package/dist/tests/testing/MultiLLMProvider.selection.test.js.map +1 -0
  292. package/dist/tests/unit/quarantine-query-builder.test.d.ts +10 -0
  293. package/dist/tests/unit/quarantine-query-builder.test.d.ts.map +1 -0
  294. package/dist/tests/unit/quarantine-query-builder.test.js +157 -0
  295. package/dist/tests/unit/quarantine-query-builder.test.js.map +1 -0
  296. package/package.json +1 -3
@@ -0,0 +1,150 @@
1
+ /**
2
+ * SMI-2756: Wave 3 — LanguageRouter tests
3
+ *
4
+ * Tests the LanguageRouter dispatch layer: adapter registration,
5
+ * extension-based lookup, tryGetAdapter, canHandle, getLanguage,
6
+ * getSupportedLanguages, framework rules aggregation, and dispose.
7
+ */
8
+ import { describe, it, expect } from 'vitest';
9
+ import { LanguageRouter } from '../../src/analysis/router.js';
10
+ describe('LanguageRouter', () => {
11
+ // -------------------------------------------------------------------------
12
+ // createWithAllAdapters factory
13
+ // -------------------------------------------------------------------------
14
+ describe('createWithAllAdapters', () => {
15
+ it('creates a router with multiple adapters pre-registered', () => {
16
+ const router = LanguageRouter.createWithAllAdapters();
17
+ expect(router.adapterCount).toBeGreaterThan(0);
18
+ });
19
+ it('creates a router with only specified languages', () => {
20
+ const router = LanguageRouter.createWithAllAdapters({ languages: ['typescript'] });
21
+ expect(router.getSupportedLanguages()).toContain('typescript');
22
+ // Should have fewer adapters than the full set
23
+ const fullRouter = LanguageRouter.createWithAllAdapters();
24
+ expect(router.adapterCount).toBeLessThanOrEqual(fullRouter.adapterCount);
25
+ });
26
+ });
27
+ // -------------------------------------------------------------------------
28
+ // registerAdapter / unregisterAdapter
29
+ // -------------------------------------------------------------------------
30
+ describe('registerAdapter / unregisterAdapter', () => {
31
+ it('registers an adapter and makes its extension routable', () => {
32
+ const router = LanguageRouter.createWithAllAdapters({ languages: ['typescript'] });
33
+ expect(router.canHandle('main.ts')).toBe(true);
34
+ });
35
+ it('unregisterAdapter removes extension mappings', () => {
36
+ const router = LanguageRouter.createWithAllAdapters({ languages: ['typescript'] });
37
+ expect(router.canHandle('main.ts')).toBe(true);
38
+ const removed = router.unregisterAdapter('typescript');
39
+ expect(removed).toBe(true);
40
+ expect(router.canHandle('main.ts')).toBe(false);
41
+ });
42
+ it('unregisterAdapter returns false for unknown language', () => {
43
+ const router = new LanguageRouter();
44
+ const result = router.unregisterAdapter('typescript');
45
+ expect(result).toBe(false);
46
+ });
47
+ it('re-registering an adapter replaces the old one', () => {
48
+ const router = LanguageRouter.createWithAllAdapters({ languages: ['typescript'] });
49
+ const before = router.adapterCount;
50
+ // Re-register the same language
51
+ const tsAdapter = router.getAdapterByLanguage('typescript');
52
+ router.registerAdapter(tsAdapter);
53
+ // Count should not grow
54
+ expect(router.adapterCount).toBe(before);
55
+ });
56
+ });
57
+ // -------------------------------------------------------------------------
58
+ // getAdapter / tryGetAdapter / canHandle / getLanguage
59
+ // -------------------------------------------------------------------------
60
+ describe('getAdapter', () => {
61
+ it('returns adapter for a known extension', () => {
62
+ const router = LanguageRouter.createWithAllAdapters({ languages: ['typescript'] });
63
+ const adapter = router.tryGetAdapter('main.ts');
64
+ expect(adapter).not.toBeNull();
65
+ expect(adapter?.language).toBe('typescript');
66
+ });
67
+ it('throws for unknown extension', () => {
68
+ const router = new LanguageRouter();
69
+ expect(() => router.getAdapter('unknown.xyz')).toThrow(/No adapter registered/);
70
+ });
71
+ it('tryGetAdapter returns null for unknown extension', () => {
72
+ const router = new LanguageRouter();
73
+ expect(router.tryGetAdapter('unknown.xyz')).toBeNull();
74
+ });
75
+ });
76
+ describe('canHandle', () => {
77
+ it('returns true for registered extension', () => {
78
+ const router = LanguageRouter.createWithAllAdapters({ languages: ['python'] });
79
+ expect(router.canHandle('app.py')).toBe(true);
80
+ });
81
+ it('returns false for unregistered extension', () => {
82
+ const router = new LanguageRouter();
83
+ expect(router.canHandle('app.cobol')).toBe(false);
84
+ });
85
+ });
86
+ describe('getLanguage', () => {
87
+ it('returns language for a known file path', () => {
88
+ const router = LanguageRouter.createWithAllAdapters({ languages: ['python'] });
89
+ expect(router.getLanguage('script.py')).toBe('python');
90
+ });
91
+ it('returns null for unsupported extension', () => {
92
+ const router = new LanguageRouter();
93
+ expect(router.getLanguage('file.cobol')).toBeNull();
94
+ });
95
+ });
96
+ // -------------------------------------------------------------------------
97
+ // getSupportedLanguages / getSupportedExtensions
98
+ // -------------------------------------------------------------------------
99
+ describe('getSupportedLanguages / getSupportedExtensions', () => {
100
+ it('getSupportedLanguages returns registered language keys', () => {
101
+ const router = LanguageRouter.createWithAllAdapters({ languages: ['typescript', 'python'] });
102
+ const langs = router.getSupportedLanguages();
103
+ expect(langs).toContain('typescript');
104
+ expect(langs).toContain('python');
105
+ });
106
+ it('getSupportedExtensions includes dot-prefixed extensions', () => {
107
+ const router = LanguageRouter.createWithAllAdapters({ languages: ['typescript'] });
108
+ const exts = router.getSupportedExtensions();
109
+ expect(exts.some((e) => e.startsWith('.'))).toBe(true);
110
+ });
111
+ });
112
+ // -------------------------------------------------------------------------
113
+ // getAllFrameworkRules / getFrameworkRules
114
+ // -------------------------------------------------------------------------
115
+ describe('framework rules', () => {
116
+ it('getAllFrameworkRules returns an array', () => {
117
+ const router = LanguageRouter.createWithAllAdapters();
118
+ const rules = router.getAllFrameworkRules();
119
+ expect(Array.isArray(rules)).toBe(true);
120
+ });
121
+ it('getFrameworkRules returns empty array for unregistered language', () => {
122
+ const router = new LanguageRouter();
123
+ const rules = router.getFrameworkRules('typescript');
124
+ expect(rules).toEqual([]);
125
+ });
126
+ });
127
+ // -------------------------------------------------------------------------
128
+ // dispose
129
+ // -------------------------------------------------------------------------
130
+ describe('dispose', () => {
131
+ it('clears adapter registry after dispose', () => {
132
+ const router = LanguageRouter.createWithAllAdapters();
133
+ expect(router.adapterCount).toBeGreaterThan(0);
134
+ router.dispose();
135
+ expect(router.adapterCount).toBe(0);
136
+ });
137
+ });
138
+ // -------------------------------------------------------------------------
139
+ // detectLanguageFromContent (disabled)
140
+ // -------------------------------------------------------------------------
141
+ describe('detectLanguageFromContent', () => {
142
+ it('returns language:null and method:none when detection is disabled', () => {
143
+ const router = new LanguageRouter({ enableLanguageDetection: false });
144
+ const result = router.detectLanguageFromContent('#!/usr/bin/env python3\nprint("hello")');
145
+ expect(result.language).toBeNull();
146
+ expect(result.method).toBe('none');
147
+ });
148
+ });
149
+ });
150
+ //# sourceMappingURL=LanguageRouter.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LanguageRouter.test.js","sourceRoot":"","sources":["../../../tests/routing/LanguageRouter.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * SMI-2754: SessionManager Memory Operation Tests
3
+ *
4
+ * Tests for storeMemoryEntry, retrieveMemoryEntry, deleteMemoryEntry,
5
+ * runPreTaskHook, and runPostTaskHook.
6
+ *
7
+ * Since USE_V3_API is evaluated at module load time, we mock the helpers
8
+ * module to control getClaudeFlowMemory / getClaudeFlowMcp / USE_V3_API.
9
+ */
10
+ export {};
11
+ //# sourceMappingURL=SessionManager.memory.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SessionManager.memory.test.d.ts","sourceRoot":"","sources":["../../../tests/session/SessionManager.memory.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
@@ -0,0 +1,219 @@
1
+ /**
2
+ * SMI-2754: SessionManager Memory Operation Tests
3
+ *
4
+ * Tests for storeMemoryEntry, retrieveMemoryEntry, deleteMemoryEntry,
5
+ * runPreTaskHook, and runPostTaskHook.
6
+ *
7
+ * Since USE_V3_API is evaluated at module load time, we mock the helpers
8
+ * module to control getClaudeFlowMemory / getClaudeFlowMcp / USE_V3_API.
9
+ */
10
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
11
+ // ============================================================================
12
+ // We mock the helpers module to control USE_V3_API and module loading
13
+ // ============================================================================
14
+ vi.mock('../../src/session/SessionManager.helpers.js', () => {
15
+ return {
16
+ validateMemoryKey: (key) => /^[a-zA-Z0-9/_-]+$/.test(key) && key.length <= 256,
17
+ MEMORY_KEYS: { CURRENT: 'session/current' },
18
+ MEMORY_NAMESPACE: 'skillsmith-sessions',
19
+ USE_V3_API: false, // Start with spawn path; override per test
20
+ getClaudeFlowMemory: vi.fn(),
21
+ getClaudeFlowMcp: vi.fn(),
22
+ };
23
+ });
24
+ import { storeMemoryEntry, retrieveMemoryEntry, deleteMemoryEntry, runPreTaskHook, runPostTaskHook, } from '../../src/session/SessionManager.memory.js';
25
+ import * as helpers from '../../src/session/SessionManager.helpers.js';
26
+ // ============================================================================
27
+ // Helpers
28
+ // ============================================================================
29
+ function makeSpawnExecutor(overrides = {}) {
30
+ return {
31
+ execute: vi.fn().mockResolvedValue({ stdout: '', stderr: '' }),
32
+ spawn: vi.fn().mockResolvedValue({ stdout: overrides.stdout ?? '', stderr: '' }),
33
+ };
34
+ }
35
+ function enableV3(enabled) {
36
+ // Override the USE_V3_API value in the mocked module
37
+ // @ts-expect-error - overriding readonly for testing
38
+ helpers.USE_V3_API = enabled;
39
+ }
40
+ beforeEach(() => {
41
+ enableV3(false);
42
+ vi.mocked(helpers.getClaudeFlowMemory).mockReset();
43
+ vi.mocked(helpers.getClaudeFlowMcp).mockReset();
44
+ });
45
+ afterEach(() => {
46
+ vi.clearAllMocks();
47
+ });
48
+ // ============================================================================
49
+ // storeMemoryEntry
50
+ // ============================================================================
51
+ describe('storeMemoryEntry', () => {
52
+ it('returns { success: false, error: "Invalid memory key" } for an invalid key', async () => {
53
+ const executor = makeSpawnExecutor();
54
+ const result = await storeMemoryEntry('bad key!', 'value', executor);
55
+ expect(result).toEqual({ success: false, error: 'Invalid memory key' });
56
+ expect(executor.spawn).not.toHaveBeenCalled();
57
+ });
58
+ it('uses V3 storeEntry when USE_V3_API is true and module is available', async () => {
59
+ enableV3(true);
60
+ const storeEntry = vi.fn().mockResolvedValue({ success: true });
61
+ vi.mocked(helpers.getClaudeFlowMemory).mockResolvedValue({ storeEntry });
62
+ const executor = makeSpawnExecutor();
63
+ const result = await storeMemoryEntry('session/test', 'my-value', executor);
64
+ expect(result).toEqual({ success: true });
65
+ expect(storeEntry).toHaveBeenCalledWith({
66
+ key: 'session/test',
67
+ value: 'my-value',
68
+ namespace: 'skillsmith-sessions',
69
+ });
70
+ expect(executor.spawn).not.toHaveBeenCalled();
71
+ });
72
+ it('falls back to spawn when V3 storeEntry returns success:false', async () => {
73
+ enableV3(true);
74
+ const storeEntry = vi.fn().mockResolvedValue({ success: false, error: 'V3 error' });
75
+ vi.mocked(helpers.getClaudeFlowMemory).mockResolvedValue({ storeEntry });
76
+ const executor = makeSpawnExecutor();
77
+ const result = await storeMemoryEntry('session/fallback', 'value', executor);
78
+ expect(result).toEqual({ success: true });
79
+ expect(executor.spawn).toHaveBeenCalled();
80
+ });
81
+ it('uses spawn directly when USE_V3_API is false', async () => {
82
+ enableV3(false);
83
+ const executor = makeSpawnExecutor();
84
+ const result = await storeMemoryEntry('session/key', 'val', executor);
85
+ expect(result).toEqual({ success: true });
86
+ expect(executor.spawn).toHaveBeenCalledWith('npx', expect.arrayContaining(['claude-flow', 'memory', 'store', '--key', 'session/key']));
87
+ expect(helpers.getClaudeFlowMemory).not.toHaveBeenCalled();
88
+ });
89
+ });
90
+ // ============================================================================
91
+ // retrieveMemoryEntry
92
+ // ============================================================================
93
+ describe('retrieveMemoryEntry', () => {
94
+ it('returns { success: false, error: "Invalid memory key" } for an invalid key', async () => {
95
+ const executor = makeSpawnExecutor();
96
+ const result = await retrieveMemoryEntry('bad key!', executor);
97
+ expect(result).toEqual({ success: false, error: 'Invalid memory key' });
98
+ });
99
+ it('returns data from V3 getEntry when found', async () => {
100
+ enableV3(true);
101
+ const getEntry = vi.fn().mockResolvedValue({
102
+ success: true,
103
+ found: true,
104
+ entry: { content: 'stored-value' },
105
+ });
106
+ vi.mocked(helpers.getClaudeFlowMemory).mockResolvedValue({ getEntry });
107
+ const executor = makeSpawnExecutor();
108
+ const result = await retrieveMemoryEntry('session/found', executor);
109
+ expect(result).toEqual({ success: true, data: 'stored-value' });
110
+ expect(executor.spawn).not.toHaveBeenCalled();
111
+ });
112
+ it('returns not-found error from V3 when key does not exist', async () => {
113
+ enableV3(true);
114
+ const getEntry = vi.fn().mockResolvedValue({
115
+ success: true,
116
+ found: false,
117
+ });
118
+ vi.mocked(helpers.getClaudeFlowMemory).mockResolvedValue({ getEntry });
119
+ const executor = makeSpawnExecutor();
120
+ const result = await retrieveMemoryEntry('session/missing', executor);
121
+ expect(result).toEqual({ success: false, error: 'Key not found' });
122
+ expect(executor.spawn).not.toHaveBeenCalled();
123
+ });
124
+ it('falls back to spawn when V3 getEntry throws an exception', async () => {
125
+ enableV3(true);
126
+ vi.mocked(helpers.getClaudeFlowMemory).mockRejectedValue(new Error('module load error'));
127
+ const executor = makeSpawnExecutor({ stdout: 'spawned-data' });
128
+ const result = await retrieveMemoryEntry('session/key', executor);
129
+ expect(result).toEqual({ success: true, data: 'spawned-data' });
130
+ expect(executor.spawn).toHaveBeenCalled();
131
+ });
132
+ it('uses spawn directly when USE_V3_API is false', async () => {
133
+ enableV3(false);
134
+ const executor = makeSpawnExecutor({ stdout: 'value-from-spawn\n' });
135
+ const result = await retrieveMemoryEntry('session/key', executor);
136
+ expect(result).toEqual({ success: true, data: 'value-from-spawn' });
137
+ expect(helpers.getClaudeFlowMemory).not.toHaveBeenCalled();
138
+ });
139
+ });
140
+ // ============================================================================
141
+ // deleteMemoryEntry
142
+ // ============================================================================
143
+ describe('deleteMemoryEntry', () => {
144
+ it('returns { success: false, error: "Invalid memory key" } for an invalid key', async () => {
145
+ const executor = makeSpawnExecutor();
146
+ const result = await deleteMemoryEntry('bad key!', executor);
147
+ expect(result).toEqual({ success: false, error: 'Invalid memory key' });
148
+ });
149
+ it('returns success when V3 callMCPTool succeeds', async () => {
150
+ enableV3(true);
151
+ const callMCPTool = vi.fn().mockResolvedValue({ success: true, deleted: true });
152
+ vi.mocked(helpers.getClaudeFlowMcp).mockResolvedValue({ callMCPTool });
153
+ const executor = makeSpawnExecutor();
154
+ const result = await deleteMemoryEntry('session/to-delete', executor);
155
+ expect(result).toEqual({ success: true });
156
+ expect(callMCPTool).toHaveBeenCalledWith('memory/delete', { key: 'session/to-delete' });
157
+ });
158
+ it('falls back to spawn (and returns success) when V3 callMCPTool fails', async () => {
159
+ enableV3(true);
160
+ const callMCPTool = vi.fn().mockResolvedValue({ success: false });
161
+ vi.mocked(helpers.getClaudeFlowMcp).mockResolvedValue({ callMCPTool });
162
+ const executor = makeSpawnExecutor();
163
+ const result = await deleteMemoryEntry('session/key', executor);
164
+ expect(result).toEqual({ success: true });
165
+ expect(executor.spawn).toHaveBeenCalled();
166
+ });
167
+ });
168
+ // ============================================================================
169
+ // runPreTaskHook
170
+ // ============================================================================
171
+ describe('runPreTaskHook', () => {
172
+ it('calls V3 MCP pre-task hook when USE_V3_API is true', async () => {
173
+ enableV3(true);
174
+ const callMCPTool = vi.fn().mockResolvedValue({});
175
+ vi.mocked(helpers.getClaudeFlowMcp).mockResolvedValue({ callMCPTool });
176
+ const executor = makeSpawnExecutor();
177
+ await runPreTaskHook('test description', executor);
178
+ expect(callMCPTool).toHaveBeenCalledWith('hooks/pre-task', expect.objectContaining({
179
+ description: 'test description',
180
+ }));
181
+ expect(executor.spawn).not.toHaveBeenCalled();
182
+ });
183
+ it('uses spawn when USE_V3_API is false', async () => {
184
+ enableV3(false);
185
+ const executor = makeSpawnExecutor();
186
+ await runPreTaskHook('test description', executor);
187
+ expect(executor.spawn).toHaveBeenCalledWith('npx', expect.arrayContaining(['pre-task', '--description', 'test description']));
188
+ });
189
+ it('swallows errors and does not throw', async () => {
190
+ enableV3(false);
191
+ const executor = {
192
+ execute: vi.fn().mockResolvedValue({ stdout: '', stderr: '' }),
193
+ spawn: vi.fn().mockRejectedValue(new Error('hook failed')),
194
+ };
195
+ // Should not throw
196
+ await expect(runPreTaskHook('test', executor)).resolves.toBeUndefined();
197
+ });
198
+ });
199
+ // ============================================================================
200
+ // runPostTaskHook
201
+ // ============================================================================
202
+ describe('runPostTaskHook', () => {
203
+ it('calls V3 MCP post-task hook when USE_V3_API is true', async () => {
204
+ enableV3(true);
205
+ const callMCPTool = vi.fn().mockResolvedValue({});
206
+ vi.mocked(helpers.getClaudeFlowMcp).mockResolvedValue({ callMCPTool });
207
+ const executor = makeSpawnExecutor();
208
+ await runPostTaskHook('task-123', executor);
209
+ expect(callMCPTool).toHaveBeenCalledWith('hooks/post-task', { taskId: 'task-123' });
210
+ expect(executor.spawn).not.toHaveBeenCalled();
211
+ });
212
+ it('uses spawn when USE_V3_API is false', async () => {
213
+ enableV3(false);
214
+ const executor = makeSpawnExecutor();
215
+ await runPostTaskHook('task-456', executor);
216
+ expect(executor.spawn).toHaveBeenCalledWith('npx', expect.arrayContaining(['post-task', '--task-id', 'task-456']));
217
+ });
218
+ });
219
+ //# sourceMappingURL=SessionManager.memory.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SessionManager.memory.test.js","sourceRoot":"","sources":["../../../tests/session/SessionManager.memory.test.ts"],"names":[],"mappings":"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"}
@@ -9,6 +9,20 @@ import { SyncConfigRepository } from '../../src/repositories/SyncConfigRepositor
9
9
  import { SyncHistoryRepository } from '../../src/repositories/SyncHistoryRepository.js';
10
10
  import { SkillRepository } from '../../src/repositories/SkillRepository.js';
11
11
  import { SyncEngine } from '../../src/sync/SyncEngine.js';
12
+ /**
13
+ * Create a mock SkillVersionRepository for testing.
14
+ * All methods return resolved promises so SyncEngine can call recordVersion
15
+ * after each upsert without errors.
16
+ */
17
+ function createMockSkillVersionRepo() {
18
+ return {
19
+ recordVersion: vi.fn().mockResolvedValue(undefined),
20
+ pruneVersions: vi.fn().mockResolvedValue(undefined),
21
+ getLatestVersion: vi.fn().mockResolvedValue(null),
22
+ getVersionHistory: vi.fn().mockResolvedValue([]),
23
+ getVersionByHash: vi.fn().mockResolvedValue(null),
24
+ };
25
+ }
12
26
  /**
13
27
  * Create a mock skill for testing
14
28
  * Note: quality_score must be between 0 and 1 (database constraint)
@@ -71,21 +85,21 @@ describe('SyncEngine', () => {
71
85
  describe('constructor', () => {
72
86
  it('should create sync engine with all dependencies', () => {
73
87
  const apiClient = createMockApiClient();
74
- const engine = new SyncEngine(apiClient, skillRepo, syncConfigRepo, syncHistoryRepo);
88
+ const engine = new SyncEngine(apiClient, skillRepo, syncConfigRepo, syncHistoryRepo, createMockSkillVersionRepo());
75
89
  expect(engine).toBeDefined();
76
90
  });
77
91
  });
78
92
  describe('sync - offline and health checks', () => {
79
93
  it('should fail when API client is offline', async () => {
80
94
  const apiClient = createMockApiClient({ offline: true });
81
- const engine = new SyncEngine(apiClient, skillRepo, syncConfigRepo, syncHistoryRepo);
95
+ const engine = new SyncEngine(apiClient, skillRepo, syncConfigRepo, syncHistoryRepo, createMockSkillVersionRepo());
82
96
  const result = await engine.sync();
83
97
  expect(result.success).toBe(false);
84
98
  expect(result.errors).toContain('API client is in offline mode. Cannot sync.');
85
99
  });
86
100
  it('should fail when API health check fails', async () => {
87
101
  const apiClient = createMockApiClient({ healthStatus: 'unhealthy' });
88
- const engine = new SyncEngine(apiClient, skillRepo, syncConfigRepo, syncHistoryRepo);
102
+ const engine = new SyncEngine(apiClient, skillRepo, syncConfigRepo, syncHistoryRepo, createMockSkillVersionRepo());
89
103
  const result = await engine.sync();
90
104
  expect(result.success).toBe(false);
91
105
  expect(result.errors).toContain('API is unhealthy. Try again later.');
@@ -99,7 +113,7 @@ describe('SyncEngine', () => {
99
113
  createMockSkill('test/skill-3'),
100
114
  ];
101
115
  const apiClient = createMockApiClient({ skills });
102
- const engine = new SyncEngine(apiClient, skillRepo, syncConfigRepo, syncHistoryRepo);
116
+ const engine = new SyncEngine(apiClient, skillRepo, syncConfigRepo, syncHistoryRepo, createMockSkillVersionRepo());
103
117
  const result = await engine.sync();
104
118
  expect(result.success).toBe(true);
105
119
  expect(result.skillsAdded).toBe(3);
@@ -119,7 +133,7 @@ describe('SyncEngine', () => {
119
133
  // API returns updated version with new updated_at
120
134
  const skills = [createMockSkill('test/skill-1', new Date(Date.now() + 1000).toISOString())];
121
135
  const apiClient = createMockApiClient({ skills });
122
- const engine = new SyncEngine(apiClient, skillRepo, syncConfigRepo, syncHistoryRepo);
136
+ const engine = new SyncEngine(apiClient, skillRepo, syncConfigRepo, syncHistoryRepo, createMockSkillVersionRepo());
123
137
  const result = await engine.sync();
124
138
  expect(result.success).toBe(true);
125
139
  expect(result.skillsAdded).toBe(0);
@@ -139,7 +153,7 @@ describe('SyncEngine', () => {
139
153
  // API returns skill with same timestamp
140
154
  const skills = [createMockSkill('test/skill-1', timestamp)];
141
155
  const apiClient = createMockApiClient({ skills });
142
- const engine = new SyncEngine(apiClient, skillRepo, syncConfigRepo, syncHistoryRepo);
156
+ const engine = new SyncEngine(apiClient, skillRepo, syncConfigRepo, syncHistoryRepo, createMockSkillVersionRepo());
143
157
  const result = await engine.sync();
144
158
  expect(result.success).toBe(true);
145
159
  expect(result.skillsUnchanged).toBe(1);
@@ -156,7 +170,7 @@ describe('SyncEngine', () => {
156
170
  const oldSkill = createMockSkill('test/old-skill', new Date(Date.now() - 2 * 60 * 60 * 1000).toISOString());
157
171
  const newSkill = createMockSkill('test/new-skill', new Date().toISOString());
158
172
  const apiClient = createMockApiClient({ skills: [oldSkill, newSkill] });
159
- const engine = new SyncEngine(apiClient, skillRepo, syncConfigRepo, syncHistoryRepo);
173
+ const engine = new SyncEngine(apiClient, skillRepo, syncConfigRepo, syncHistoryRepo, createMockSkillVersionRepo());
160
174
  const result = await engine.sync();
161
175
  expect(result.success).toBe(true);
162
176
  // Only the new skill should be added (old one filtered out by differential)
@@ -173,7 +187,7 @@ describe('SyncEngine', () => {
173
187
  createMockSkill('test/skill-2', new Date(Date.now() - 2 * 60 * 60 * 1000).toISOString()),
174
188
  ];
175
189
  const apiClient = createMockApiClient({ skills });
176
- const engine = new SyncEngine(apiClient, skillRepo, syncConfigRepo, syncHistoryRepo);
190
+ const engine = new SyncEngine(apiClient, skillRepo, syncConfigRepo, syncHistoryRepo, createMockSkillVersionRepo());
177
191
  const result = await engine.sync({ force: true });
178
192
  expect(result.success).toBe(true);
179
193
  expect(result.skillsAdded).toBe(2); // All skills processed because force=true
@@ -183,7 +197,7 @@ describe('SyncEngine', () => {
183
197
  it('should not modify database in dry run mode', async () => {
184
198
  const skills = [createMockSkill('test/skill-1')];
185
199
  const apiClient = createMockApiClient({ skills });
186
- const engine = new SyncEngine(apiClient, skillRepo, syncConfigRepo, syncHistoryRepo);
200
+ const engine = new SyncEngine(apiClient, skillRepo, syncConfigRepo, syncHistoryRepo, createMockSkillVersionRepo());
187
201
  const result = await engine.sync({ dryRun: true });
188
202
  expect(result.success).toBe(true);
189
203
  expect(result.dryRun).toBe(true);
@@ -200,7 +214,7 @@ describe('SyncEngine', () => {
200
214
  it('should call onProgress callback with phases', async () => {
201
215
  const skills = [createMockSkill('test/skill-1')];
202
216
  const apiClient = createMockApiClient({ skills });
203
- const engine = new SyncEngine(apiClient, skillRepo, syncConfigRepo, syncHistoryRepo);
217
+ const engine = new SyncEngine(apiClient, skillRepo, syncConfigRepo, syncHistoryRepo, createMockSkillVersionRepo());
204
218
  const progressCalls = [];
205
219
  const onProgress = vi.fn((progress) => {
206
220
  progressCalls.push(progress.phase);
@@ -219,7 +233,7 @@ describe('SyncEngine', () => {
219
233
  // Create 150 skills (more than one page of 100)
220
234
  const skills = Array.from({ length: 150 }, (_, i) => createMockSkill(`test/skill-${i}`));
221
235
  const apiClient = createMockApiClient({ skills });
222
- const engine = new SyncEngine(apiClient, skillRepo, syncConfigRepo, syncHistoryRepo);
236
+ const engine = new SyncEngine(apiClient, skillRepo, syncConfigRepo, syncHistoryRepo, createMockSkillVersionRepo());
223
237
  const result = await engine.sync({ pageSize: 100 });
224
238
  expect(result.success).toBe(true);
225
239
  expect(result.skillsAdded).toBe(150);
@@ -234,7 +248,7 @@ describe('SyncEngine', () => {
234
248
  it('should record sync history on success', async () => {
235
249
  const skills = [createMockSkill('test/skill-1')];
236
250
  const apiClient = createMockApiClient({ skills });
237
- const engine = new SyncEngine(apiClient, skillRepo, syncConfigRepo, syncHistoryRepo);
251
+ const engine = new SyncEngine(apiClient, skillRepo, syncConfigRepo, syncHistoryRepo, createMockSkillVersionRepo());
238
252
  await engine.sync();
239
253
  const history = syncHistoryRepo.getHistory();
240
254
  expect(history).toHaveLength(1);
@@ -245,7 +259,7 @@ describe('SyncEngine', () => {
245
259
  const apiClient = createMockApiClient({
246
260
  throwOnSearch: new Error('Network failure'),
247
261
  });
248
- const engine = new SyncEngine(apiClient, skillRepo, syncConfigRepo, syncHistoryRepo);
262
+ const engine = new SyncEngine(apiClient, skillRepo, syncConfigRepo, syncHistoryRepo, createMockSkillVersionRepo());
249
263
  await engine.sync();
250
264
  const history = syncHistoryRepo.getHistory();
251
265
  expect(history).toHaveLength(1);
@@ -255,7 +269,7 @@ describe('SyncEngine', () => {
255
269
  it('should update sync config on success', async () => {
256
270
  const skills = [createMockSkill('test/skill-1')];
257
271
  const apiClient = createMockApiClient({ skills });
258
- const engine = new SyncEngine(apiClient, skillRepo, syncConfigRepo, syncHistoryRepo);
272
+ const engine = new SyncEngine(apiClient, skillRepo, syncConfigRepo, syncHistoryRepo, createMockSkillVersionRepo());
259
273
  await engine.sync();
260
274
  const config = syncConfigRepo.getConfig();
261
275
  expect(config.lastSyncAt).not.toBeNull();
@@ -264,7 +278,7 @@ describe('SyncEngine', () => {
264
278
  });
265
279
  it('should set error in config on failure', async () => {
266
280
  const apiClient = createMockApiClient({ offline: true });
267
- const engine = new SyncEngine(apiClient, skillRepo, syncConfigRepo, syncHistoryRepo);
281
+ const engine = new SyncEngine(apiClient, skillRepo, syncConfigRepo, syncHistoryRepo, createMockSkillVersionRepo());
268
282
  await engine.sync();
269
283
  const config = syncConfigRepo.getConfig();
270
284
  expect(config.lastSyncError).toBe('API client is in offline mode. Cannot sync.');
@@ -273,7 +287,7 @@ describe('SyncEngine', () => {
273
287
  describe('getStatus', () => {
274
288
  it('should return sync status summary', () => {
275
289
  const apiClient = createMockApiClient();
276
- const engine = new SyncEngine(apiClient, skillRepo, syncConfigRepo, syncHistoryRepo);
290
+ const engine = new SyncEngine(apiClient, skillRepo, syncConfigRepo, syncHistoryRepo, createMockSkillVersionRepo());
277
291
  const status = engine.getStatus();
278
292
  expect(status).toHaveProperty('config');
279
293
  expect(status).toHaveProperty('lastRun');
@@ -282,14 +296,14 @@ describe('SyncEngine', () => {
282
296
  });
283
297
  it('should show sync is due when never synced', () => {
284
298
  const apiClient = createMockApiClient();
285
- const engine = new SyncEngine(apiClient, skillRepo, syncConfigRepo, syncHistoryRepo);
299
+ const engine = new SyncEngine(apiClient, skillRepo, syncConfigRepo, syncHistoryRepo, createMockSkillVersionRepo());
286
300
  const status = engine.getStatus();
287
301
  expect(status.isDue).toBe(true);
288
302
  expect(status.lastRun).toBeNull();
289
303
  });
290
304
  it('should reflect running state', async () => {
291
305
  const apiClient = createMockApiClient();
292
- const engine = new SyncEngine(apiClient, skillRepo, syncConfigRepo, syncHistoryRepo);
306
+ const engine = new SyncEngine(apiClient, skillRepo, syncConfigRepo, syncHistoryRepo, createMockSkillVersionRepo());
293
307
  // Start a run manually
294
308
  syncHistoryRepo.startRun();
295
309
  const status = engine.getStatus();