@skillsmith/mcp-server 0.3.1 → 0.3.2

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 (314) hide show
  1. package/dist/.tsbuildinfo +1 -0
  2. package/dist/src/__tests__/get-skill.test.d.ts +6 -0
  3. package/dist/src/__tests__/get-skill.test.d.ts.map +1 -0
  4. package/dist/src/__tests__/get-skill.test.js +88 -0
  5. package/dist/src/__tests__/get-skill.test.js.map +1 -0
  6. package/dist/src/__tests__/middleware/errorFormatter.test.d.ts +7 -0
  7. package/dist/src/__tests__/middleware/errorFormatter.test.d.ts.map +1 -0
  8. package/dist/src/__tests__/middleware/errorFormatter.test.js +304 -0
  9. package/dist/src/__tests__/middleware/errorFormatter.test.js.map +1 -0
  10. package/dist/src/__tests__/middleware/license.test.d.ts +7 -0
  11. package/dist/src/__tests__/middleware/license.test.d.ts.map +1 -0
  12. package/dist/src/__tests__/middleware/license.test.js +500 -0
  13. package/dist/src/__tests__/middleware/license.test.js.map +1 -0
  14. package/dist/src/__tests__/search.test.d.ts +6 -0
  15. package/dist/src/__tests__/search.test.d.ts.map +1 -0
  16. package/dist/src/__tests__/search.test.js +86 -0
  17. package/dist/src/__tests__/search.test.js.map +1 -0
  18. package/dist/src/__tests__/test-utils.d.ts +20 -0
  19. package/dist/src/__tests__/test-utils.d.ts.map +1 -0
  20. package/dist/src/__tests__/test-utils.js +91 -0
  21. package/dist/src/__tests__/test-utils.js.map +1 -0
  22. package/dist/src/context/index.d.ts +19 -0
  23. package/dist/src/context/index.d.ts.map +1 -0
  24. package/dist/src/context/index.js +25 -0
  25. package/dist/src/context/index.js.map +1 -0
  26. package/dist/src/context/project-detector.d.ts +145 -0
  27. package/dist/src/context/project-detector.d.ts.map +1 -0
  28. package/dist/src/context/project-detector.js +321 -0
  29. package/dist/src/context/project-detector.js.map +1 -0
  30. package/dist/src/context.d.ts +157 -0
  31. package/dist/src/context.d.ts.map +1 -0
  32. package/dist/src/context.js +231 -0
  33. package/dist/src/context.js.map +1 -0
  34. package/dist/src/core-shim.d.ts +7 -0
  35. package/dist/src/core-shim.d.ts.map +1 -0
  36. package/dist/src/core-shim.js +9 -0
  37. package/dist/src/core-shim.js.map +1 -0
  38. package/dist/src/health/healthCheck.d.ts +88 -0
  39. package/dist/src/health/healthCheck.d.ts.map +1 -0
  40. package/dist/src/health/healthCheck.js +117 -0
  41. package/dist/src/health/healthCheck.js.map +1 -0
  42. package/dist/src/health/index.d.ts +21 -0
  43. package/dist/src/health/index.d.ts.map +1 -0
  44. package/dist/src/health/index.js +21 -0
  45. package/dist/src/health/index.js.map +1 -0
  46. package/dist/src/health/readinessCheck.d.ts +139 -0
  47. package/dist/src/health/readinessCheck.d.ts.map +1 -0
  48. package/dist/src/health/readinessCheck.js +266 -0
  49. package/dist/src/health/readinessCheck.js.map +1 -0
  50. package/dist/src/index.d.ts +10 -0
  51. package/dist/src/index.d.ts.map +1 -0
  52. package/dist/src/index.js +237 -0
  53. package/dist/src/index.js.map +1 -0
  54. package/dist/src/index.test.d.ts +2 -0
  55. package/dist/src/index.test.d.ts.map +1 -0
  56. package/dist/src/index.test.js +43 -0
  57. package/dist/src/index.test.js.map +1 -0
  58. package/dist/src/logger.d.ts +26 -0
  59. package/dist/src/logger.d.ts.map +1 -0
  60. package/dist/src/logger.js +179 -0
  61. package/dist/src/logger.js.map +1 -0
  62. package/dist/src/middleware/__tests__/csp.test.d.ts +2 -0
  63. package/dist/src/middleware/__tests__/csp.test.d.ts.map +1 -0
  64. package/dist/src/middleware/__tests__/csp.test.js +390 -0
  65. package/dist/src/middleware/__tests__/csp.test.js.map +1 -0
  66. package/dist/src/middleware/csp.d.ts +103 -0
  67. package/dist/src/middleware/csp.d.ts.map +1 -0
  68. package/dist/src/middleware/csp.js +273 -0
  69. package/dist/src/middleware/csp.js.map +1 -0
  70. package/dist/src/middleware/degradation.d.ts +105 -0
  71. package/dist/src/middleware/degradation.d.ts.map +1 -0
  72. package/dist/src/middleware/degradation.js +319 -0
  73. package/dist/src/middleware/degradation.js.map +1 -0
  74. package/dist/src/middleware/errorFormatter.d.ts +119 -0
  75. package/dist/src/middleware/errorFormatter.d.ts.map +1 -0
  76. package/dist/src/middleware/errorFormatter.js +294 -0
  77. package/dist/src/middleware/errorFormatter.js.map +1 -0
  78. package/dist/src/middleware/index.d.ts +11 -0
  79. package/dist/src/middleware/index.d.ts.map +1 -0
  80. package/dist/src/middleware/index.js +16 -0
  81. package/dist/src/middleware/index.js.map +1 -0
  82. package/dist/src/middleware/license.d.ts +169 -0
  83. package/dist/src/middleware/license.d.ts.map +1 -0
  84. package/dist/src/middleware/license.js +292 -0
  85. package/dist/src/middleware/license.js.map +1 -0
  86. package/dist/src/middleware/quota.d.ts +182 -0
  87. package/dist/src/middleware/quota.d.ts.map +1 -0
  88. package/dist/src/middleware/quota.js +309 -0
  89. package/dist/src/middleware/quota.js.map +1 -0
  90. package/dist/src/middleware/toolFeatureMapping.d.ts +36 -0
  91. package/dist/src/middleware/toolFeatureMapping.d.ts.map +1 -0
  92. package/dist/src/middleware/toolFeatureMapping.js +96 -0
  93. package/dist/src/middleware/toolFeatureMapping.js.map +1 -0
  94. package/dist/src/onboarding/first-run.d.ts +65 -0
  95. package/dist/src/onboarding/first-run.d.ts.map +1 -0
  96. package/dist/src/onboarding/first-run.js +79 -0
  97. package/dist/src/onboarding/first-run.js.map +1 -0
  98. package/dist/src/onboarding/index.d.ts +7 -0
  99. package/dist/src/onboarding/index.d.ts.map +1 -0
  100. package/dist/src/onboarding/index.js +7 -0
  101. package/dist/src/onboarding/index.js.map +1 -0
  102. package/dist/src/onboarding/install-assets.d.ts +27 -0
  103. package/dist/src/onboarding/install-assets.d.ts.map +1 -0
  104. package/dist/src/onboarding/install-assets.js +128 -0
  105. package/dist/src/onboarding/install-assets.js.map +1 -0
  106. package/dist/src/suggestions/index.d.ts +21 -0
  107. package/dist/src/suggestions/index.d.ts.map +1 -0
  108. package/dist/src/suggestions/index.js +20 -0
  109. package/dist/src/suggestions/index.js.map +1 -0
  110. package/dist/src/suggestions/suggestion-engine.d.ts +185 -0
  111. package/dist/src/suggestions/suggestion-engine.d.ts.map +1 -0
  112. package/dist/src/suggestions/suggestion-engine.js +352 -0
  113. package/dist/src/suggestions/suggestion-engine.js.map +1 -0
  114. package/dist/src/suggestions/types.d.ts +88 -0
  115. package/dist/src/suggestions/types.d.ts.map +1 -0
  116. package/dist/src/suggestions/types.js +21 -0
  117. package/dist/src/suggestions/types.js.map +1 -0
  118. package/dist/src/tools/analyze.d.ts +151 -0
  119. package/dist/src/tools/analyze.d.ts.map +1 -0
  120. package/dist/src/tools/analyze.js +205 -0
  121. package/dist/src/tools/analyze.js.map +1 -0
  122. package/dist/src/tools/compare.d.ts +149 -0
  123. package/dist/src/tools/compare.d.ts.map +1 -0
  124. package/dist/src/tools/compare.js +464 -0
  125. package/dist/src/tools/compare.js.map +1 -0
  126. package/dist/src/tools/get-skill.d.ts +107 -0
  127. package/dist/src/tools/get-skill.d.ts.map +1 -0
  128. package/dist/src/tools/get-skill.js +260 -0
  129. package/dist/src/tools/get-skill.js.map +1 -0
  130. package/dist/src/tools/index.d.ts +20 -0
  131. package/dist/src/tools/index.d.ts.map +1 -0
  132. package/dist/src/tools/index.js +20 -0
  133. package/dist/src/tools/index.js.map +1 -0
  134. package/dist/src/tools/install.d.ts +122 -0
  135. package/dist/src/tools/install.d.ts.map +1 -0
  136. package/dist/src/tools/install.js +326 -0
  137. package/dist/src/tools/install.js.map +1 -0
  138. package/dist/src/tools/recommend.d.ts +169 -0
  139. package/dist/src/tools/recommend.d.ts.map +1 -0
  140. package/dist/src/tools/recommend.js +357 -0
  141. package/dist/src/tools/recommend.js.map +1 -0
  142. package/dist/src/tools/search.d.ts +114 -0
  143. package/dist/src/tools/search.d.ts.map +1 -0
  144. package/dist/src/tools/search.js +247 -0
  145. package/dist/src/tools/search.js.map +1 -0
  146. package/dist/src/tools/suggest.d.ts +181 -0
  147. package/dist/src/tools/suggest.d.ts.map +1 -0
  148. package/dist/src/tools/suggest.js +310 -0
  149. package/dist/src/tools/suggest.js.map +1 -0
  150. package/dist/src/tools/uninstall.d.ts +123 -0
  151. package/dist/src/tools/uninstall.d.ts.map +1 -0
  152. package/dist/src/tools/uninstall.js +250 -0
  153. package/dist/src/tools/uninstall.js.map +1 -0
  154. package/dist/src/tools/validate.d.ts +122 -0
  155. package/dist/src/tools/validate.d.ts.map +1 -0
  156. package/dist/src/tools/validate.js +497 -0
  157. package/dist/src/tools/validate.js.map +1 -0
  158. package/dist/src/utils/installed-skills.d.ts +101 -0
  159. package/dist/src/utils/installed-skills.d.ts.map +1 -0
  160. package/dist/src/utils/installed-skills.js +220 -0
  161. package/dist/src/utils/installed-skills.js.map +1 -0
  162. package/dist/src/utils/validation.d.ts +95 -0
  163. package/dist/src/utils/validation.d.ts.map +1 -0
  164. package/dist/src/utils/validation.js +186 -0
  165. package/dist/src/utils/validation.js.map +1 -0
  166. package/dist/src/webhooks/index.d.ts +8 -0
  167. package/dist/src/webhooks/index.d.ts.map +1 -0
  168. package/dist/src/webhooks/index.js +9 -0
  169. package/dist/src/webhooks/index.js.map +1 -0
  170. package/dist/src/webhooks/webhook-endpoint.d.ts +149 -0
  171. package/dist/src/webhooks/webhook-endpoint.d.ts.map +1 -0
  172. package/dist/src/webhooks/webhook-endpoint.js +339 -0
  173. package/dist/src/webhooks/webhook-endpoint.js.map +1 -0
  174. package/dist/tests/compare.test.d.ts +6 -0
  175. package/dist/tests/compare.test.d.ts.map +1 -0
  176. package/dist/tests/compare.test.js +225 -0
  177. package/dist/tests/compare.test.js.map +1 -0
  178. package/dist/tests/context/project-detector.test.d.ts +6 -0
  179. package/dist/tests/context/project-detector.test.d.ts.map +1 -0
  180. package/dist/tests/context/project-detector.test.js +719 -0
  181. package/dist/tests/context/project-detector.test.js.map +1 -0
  182. package/dist/tests/e2e/compare.e2e.test.d.ts +10 -0
  183. package/dist/tests/e2e/compare.e2e.test.d.ts.map +1 -0
  184. package/dist/tests/e2e/compare.e2e.test.js +296 -0
  185. package/dist/tests/e2e/compare.e2e.test.js.map +1 -0
  186. package/dist/tests/e2e/install-flow.e2e.test.d.ts +10 -0
  187. package/dist/tests/e2e/install-flow.e2e.test.d.ts.map +1 -0
  188. package/dist/tests/e2e/install-flow.e2e.test.js +229 -0
  189. package/dist/tests/e2e/install-flow.e2e.test.js.map +1 -0
  190. package/dist/tests/e2e/recommend.e2e.test.d.ts +12 -0
  191. package/dist/tests/e2e/recommend.e2e.test.d.ts.map +1 -0
  192. package/dist/tests/e2e/recommend.e2e.test.js +357 -0
  193. package/dist/tests/e2e/recommend.e2e.test.js.map +1 -0
  194. package/dist/tests/e2e/skill-flow.e2e.test.d.ts +10 -0
  195. package/dist/tests/e2e/skill-flow.e2e.test.d.ts.map +1 -0
  196. package/dist/tests/e2e/skill-flow.e2e.test.js +311 -0
  197. package/dist/tests/e2e/skill-flow.e2e.test.js.map +1 -0
  198. package/dist/tests/e2e/suggest.e2e.test.d.ts +13 -0
  199. package/dist/tests/e2e/suggest.e2e.test.d.ts.map +1 -0
  200. package/dist/tests/e2e/suggest.e2e.test.js +367 -0
  201. package/dist/tests/e2e/suggest.e2e.test.js.map +1 -0
  202. package/dist/tests/e2e/utils/baseline-collector.d.ts +107 -0
  203. package/dist/tests/e2e/utils/baseline-collector.d.ts.map +1 -0
  204. package/dist/tests/e2e/utils/baseline-collector.js +211 -0
  205. package/dist/tests/e2e/utils/baseline-collector.js.map +1 -0
  206. package/dist/tests/e2e/utils/hardcoded-detector.d.ts +46 -0
  207. package/dist/tests/e2e/utils/hardcoded-detector.d.ts.map +1 -0
  208. package/dist/tests/e2e/utils/hardcoded-detector.js +255 -0
  209. package/dist/tests/e2e/utils/hardcoded-detector.js.map +1 -0
  210. package/dist/tests/e2e/utils/index.d.ts +7 -0
  211. package/dist/tests/e2e/utils/index.d.ts.map +1 -0
  212. package/dist/tests/e2e/utils/index.js +7 -0
  213. package/dist/tests/e2e/utils/index.js.map +1 -0
  214. package/dist/tests/e2e/utils/linear-reporter.d.ts +60 -0
  215. package/dist/tests/e2e/utils/linear-reporter.d.ts.map +1 -0
  216. package/dist/tests/e2e/utils/linear-reporter.js +232 -0
  217. package/dist/tests/e2e/utils/linear-reporter.js.map +1 -0
  218. package/dist/tests/health.test.d.ts +9 -0
  219. package/dist/tests/health.test.d.ts.map +1 -0
  220. package/dist/tests/health.test.js +308 -0
  221. package/dist/tests/health.test.js.map +1 -0
  222. package/dist/tests/integration/analyze.integration.test.d.ts +2 -0
  223. package/dist/tests/integration/analyze.integration.test.d.ts.map +1 -0
  224. package/dist/tests/integration/analyze.integration.test.js +244 -0
  225. package/dist/tests/integration/analyze.integration.test.js.map +1 -0
  226. package/dist/tests/integration/compare.integration.test.d.ts +2 -0
  227. package/dist/tests/integration/compare.integration.test.d.ts.map +1 -0
  228. package/dist/tests/integration/compare.integration.test.js +120 -0
  229. package/dist/tests/integration/compare.integration.test.js.map +1 -0
  230. package/dist/tests/integration/fixtures/test-skills.d.ts +62 -0
  231. package/dist/tests/integration/fixtures/test-skills.d.ts.map +1 -0
  232. package/dist/tests/integration/fixtures/test-skills.js +644 -0
  233. package/dist/tests/integration/fixtures/test-skills.js.map +1 -0
  234. package/dist/tests/integration/get-skill.integration.test.d.ts +6 -0
  235. package/dist/tests/integration/get-skill.integration.test.d.ts.map +1 -0
  236. package/dist/tests/integration/get-skill.integration.test.js +203 -0
  237. package/dist/tests/integration/get-skill.integration.test.js.map +1 -0
  238. package/dist/tests/integration/github-api.integration.test.d.ts +14 -0
  239. package/dist/tests/integration/github-api.integration.test.d.ts.map +1 -0
  240. package/dist/tests/integration/github-api.integration.test.js +190 -0
  241. package/dist/tests/integration/github-api.integration.test.js.map +1 -0
  242. package/dist/tests/integration/install.integration.test.d.ts +6 -0
  243. package/dist/tests/integration/install.integration.test.d.ts.map +1 -0
  244. package/dist/tests/integration/install.integration.test.js +282 -0
  245. package/dist/tests/integration/install.integration.test.js.map +1 -0
  246. package/dist/tests/integration/recommend.integration.test.d.ts +2 -0
  247. package/dist/tests/integration/recommend.integration.test.d.ts.map +1 -0
  248. package/dist/tests/integration/recommend.integration.test.js +217 -0
  249. package/dist/tests/integration/recommend.integration.test.js.map +1 -0
  250. package/dist/tests/integration/search.integration.test.d.ts +6 -0
  251. package/dist/tests/integration/search.integration.test.d.ts.map +1 -0
  252. package/dist/tests/integration/search.integration.test.js +229 -0
  253. package/dist/tests/integration/search.integration.test.js.map +1 -0
  254. package/dist/tests/integration/setup.d.ts +74 -0
  255. package/dist/tests/integration/setup.d.ts.map +1 -0
  256. package/dist/tests/integration/setup.js +131 -0
  257. package/dist/tests/integration/setup.js.map +1 -0
  258. package/dist/tests/integration/uninstall.integration.test.d.ts +6 -0
  259. package/dist/tests/integration/uninstall.integration.test.d.ts.map +1 -0
  260. package/dist/tests/integration/uninstall.integration.test.js +296 -0
  261. package/dist/tests/integration/uninstall.integration.test.js.map +1 -0
  262. package/dist/tests/integration/validate.integration.test.d.ts +2 -0
  263. package/dist/tests/integration/validate.integration.test.d.ts.map +1 -0
  264. package/dist/tests/integration/validate.integration.test.js +181 -0
  265. package/dist/tests/integration/validate.integration.test.js.map +1 -0
  266. package/dist/tests/onboarding/first-run.test.d.ts +7 -0
  267. package/dist/tests/onboarding/first-run.test.d.ts.map +1 -0
  268. package/dist/tests/onboarding/first-run.test.js +264 -0
  269. package/dist/tests/onboarding/first-run.test.js.map +1 -0
  270. package/dist/tests/performance/search-performance.test.d.ts +10 -0
  271. package/dist/tests/performance/search-performance.test.d.ts.map +1 -0
  272. package/dist/tests/performance/search-performance.test.js +222 -0
  273. package/dist/tests/performance/search-performance.test.js.map +1 -0
  274. package/dist/tests/recommend.test.d.ts +6 -0
  275. package/dist/tests/recommend.test.d.ts.map +1 -0
  276. package/dist/tests/recommend.test.js +210 -0
  277. package/dist/tests/recommend.test.js.map +1 -0
  278. package/dist/tests/suggestions/suggestion-engine.test.d.ts +6 -0
  279. package/dist/tests/suggestions/suggestion-engine.test.d.ts.map +1 -0
  280. package/dist/tests/suggestions/suggestion-engine.test.js +448 -0
  281. package/dist/tests/suggestions/suggestion-engine.test.js.map +1 -0
  282. package/dist/tests/test-utils.d.ts +74 -0
  283. package/dist/tests/test-utils.d.ts.map +1 -0
  284. package/dist/tests/test-utils.js +98 -0
  285. package/dist/tests/test-utils.js.map +1 -0
  286. package/dist/tests/tools.test.d.ts +5 -0
  287. package/dist/tests/tools.test.d.ts.map +1 -0
  288. package/dist/tests/tools.test.js +138 -0
  289. package/dist/tests/tools.test.js.map +1 -0
  290. package/dist/tests/unit/installed-skills.test.d.ts +6 -0
  291. package/dist/tests/unit/installed-skills.test.d.ts.map +1 -0
  292. package/dist/tests/unit/installed-skills.test.js +285 -0
  293. package/dist/tests/unit/installed-skills.test.js.map +1 -0
  294. package/dist/tests/unit/logger.test.d.ts +6 -0
  295. package/dist/tests/unit/logger.test.d.ts.map +1 -0
  296. package/dist/tests/unit/logger.test.js +281 -0
  297. package/dist/tests/unit/logger.test.js.map +1 -0
  298. package/dist/tests/validate.test.d.ts +5 -0
  299. package/dist/tests/validate.test.d.ts.map +1 -0
  300. package/dist/tests/validate.test.js +303 -0
  301. package/dist/tests/validate.test.js.map +1 -0
  302. package/dist/tests/webhooks/proxy-trust.security.test.d.ts +8 -0
  303. package/dist/tests/webhooks/proxy-trust.security.test.d.ts.map +1 -0
  304. package/dist/tests/webhooks/proxy-trust.security.test.js +145 -0
  305. package/dist/tests/webhooks/proxy-trust.security.test.js.map +1 -0
  306. package/dist/tests/webhooks/rate-limiter.security.test.d.ts +8 -0
  307. package/dist/tests/webhooks/rate-limiter.security.test.d.ts.map +1 -0
  308. package/dist/tests/webhooks/rate-limiter.security.test.js +122 -0
  309. package/dist/tests/webhooks/rate-limiter.security.test.js.map +1 -0
  310. package/dist/vitest.config.d.ts +6 -0
  311. package/dist/vitest.config.d.ts.map +1 -0
  312. package/dist/vitest.config.js +13 -0
  313. package/dist/vitest.config.js.map +1 -0
  314. package/package.json +1 -1
@@ -0,0 +1,352 @@
1
+ /**
2
+ * @fileoverview Contextual Skill Suggestion Engine
3
+ * @module @skillsmith/mcp-server/suggestions/suggestion-engine
4
+ * @see SMI-913: Contextual skill suggestions after first success
5
+ *
6
+ * Provides intelligent skill recommendations based on project context detection.
7
+ * Implements rate limiting, opt-out functionality, and persistent state management.
8
+ *
9
+ * @example
10
+ * import { SuggestionEngine } from './suggestion-engine.js'
11
+ * import { detectProjectContext } from '../context/project-detector.js'
12
+ *
13
+ * const engine = new SuggestionEngine()
14
+ * const context = detectProjectContext()
15
+ * const suggestions = engine.getSuggestions(context, ['installed/skill1'])
16
+ *
17
+ * if (suggestions.length > 0) {
18
+ * console.log(`Suggestion: ${suggestions[0].skillName} - ${suggestions[0].reason}`)
19
+ * engine.recordSuggestionShown()
20
+ * }
21
+ */
22
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
23
+ import { join } from 'path';
24
+ import { homedir } from 'os';
25
+ /** Cooldown period between suggestions in milliseconds (5 minutes) */
26
+ const SUGGESTION_COOLDOWN_MS = 5 * 60 * 1000;
27
+ /** Maximum suggestions per day per user */
28
+ const MAX_SUGGESTIONS_PER_DAY = 3;
29
+ /** Default directory for storing Skillsmith configuration and state */
30
+ const DEFAULT_SUGGESTIONS_DIR = join(homedir(), '.skillsmith');
31
+ /** Default configuration values */
32
+ const DEFAULT_CONFIG = {
33
+ cooldownMs: SUGGESTION_COOLDOWN_MS,
34
+ maxSuggestionsPerDay: MAX_SUGGESTIONS_PER_DAY,
35
+ enableOptOut: true,
36
+ };
37
+ /**
38
+ * Skill suggestion rules based on project context
39
+ *
40
+ * Rules are evaluated in order. Each rule checks for specific project
41
+ * characteristics and suggests relevant skills.
42
+ */
43
+ const SUGGESTION_RULES = [
44
+ {
45
+ condition: (ctx) => ctx.hasDocker && ctx.hasNativeModules,
46
+ skillId: 'community/docker',
47
+ skillName: 'docker',
48
+ reason: 'Your project uses native modules - Docker ensures consistent builds',
49
+ priority: 1,
50
+ },
51
+ {
52
+ condition: (ctx) => ctx.hasLinear,
53
+ skillId: 'user/linear',
54
+ skillName: 'linear',
55
+ reason: 'Automate Linear issue updates from your commits',
56
+ priority: 2,
57
+ },
58
+ {
59
+ condition: (ctx) => ctx.hasGitHub,
60
+ skillId: 'anthropic/review-pr',
61
+ skillName: 'review-pr',
62
+ reason: 'Get AI-powered code review suggestions for your PRs',
63
+ priority: 2,
64
+ },
65
+ {
66
+ condition: (ctx) => ctx.testFramework === 'jest',
67
+ skillId: 'community/jest-helper',
68
+ skillName: 'jest-helper',
69
+ reason: 'Generate and improve Jest tests automatically',
70
+ priority: 3,
71
+ },
72
+ {
73
+ condition: (ctx) => ctx.testFramework === 'vitest',
74
+ skillId: 'community/vitest-helper',
75
+ skillName: 'vitest-helper',
76
+ reason: 'Generate and improve Vitest tests automatically',
77
+ priority: 3,
78
+ },
79
+ {
80
+ condition: (ctx) => ctx.apiFramework === 'express' || ctx.apiFramework === 'nextjs',
81
+ skillId: 'community/api-docs',
82
+ skillName: 'api-docs',
83
+ reason: 'Generate OpenAPI documentation for your API endpoints',
84
+ priority: 4,
85
+ },
86
+ ];
87
+ /**
88
+ * Engine for generating contextual skill suggestions
89
+ *
90
+ * Manages suggestion state, rate limiting, and skill recommendations
91
+ * based on detected project context.
92
+ *
93
+ * @example
94
+ * const engine = new SuggestionEngine({ cooldownMs: 10 * 60 * 1000 })
95
+ * const context = detectProjectContext('/path/to/project')
96
+ * const suggestions = engine.getSuggestions(context, ['installed/skill'])
97
+ */
98
+ export class SuggestionEngine {
99
+ config;
100
+ state;
101
+ stateDir;
102
+ stateFile;
103
+ /**
104
+ * Create a new SuggestionEngine instance
105
+ *
106
+ * @param config - Partial configuration to override defaults
107
+ *
108
+ * @example
109
+ * // Use defaults
110
+ * const engine = new SuggestionEngine()
111
+ *
112
+ * // Override cooldown
113
+ * const engine = new SuggestionEngine({ cooldownMs: 10 * 60 * 1000 })
114
+ *
115
+ * // Custom state directory (for testing)
116
+ * const engine = new SuggestionEngine({ stateDir: '/tmp/test-state' })
117
+ */
118
+ constructor(config = {}) {
119
+ this.config = { ...DEFAULT_CONFIG, ...config };
120
+ this.stateDir = config.stateDir || DEFAULT_SUGGESTIONS_DIR;
121
+ this.stateFile = join(this.stateDir, 'suggestions-state.json');
122
+ this.state = this.loadState();
123
+ }
124
+ /**
125
+ * Load suggestion state from disk
126
+ *
127
+ * Resets daily count if it's a new day.
128
+ *
129
+ * @returns Loaded or default suggestion state
130
+ */
131
+ loadState() {
132
+ if (existsSync(this.stateFile)) {
133
+ try {
134
+ const data = JSON.parse(readFileSync(this.stateFile, 'utf-8'));
135
+ // Reset daily count if new day
136
+ const today = new Date().toDateString();
137
+ const lastDay = new Date(data.lastSuggestionTime || 0).toDateString();
138
+ if (today !== lastDay) {
139
+ data.suggestionsToday = 0;
140
+ }
141
+ return data;
142
+ }
143
+ catch (error) {
144
+ console.warn('[suggestion-engine] Failed to load state:', this.stateFile, error instanceof Error ? error.message : String(error));
145
+ return this.getDefaultState();
146
+ }
147
+ }
148
+ return this.getDefaultState();
149
+ }
150
+ /**
151
+ * Get default suggestion state
152
+ *
153
+ * @returns Fresh default state object
154
+ */
155
+ getDefaultState() {
156
+ return {
157
+ lastSuggestionTime: 0,
158
+ suggestionsToday: 0,
159
+ optedOut: false,
160
+ dismissedSkills: [],
161
+ };
162
+ }
163
+ /**
164
+ * Save suggestion state to disk
165
+ *
166
+ * Creates the state directory if it doesn't exist.
167
+ */
168
+ saveState() {
169
+ if (!existsSync(this.stateDir)) {
170
+ mkdirSync(this.stateDir, { recursive: true });
171
+ }
172
+ writeFileSync(this.stateFile, JSON.stringify(this.state, null, 2));
173
+ }
174
+ /**
175
+ * Check if suggestions can be shown based on rate limits
176
+ *
177
+ * Checks:
178
+ * - User has not opted out
179
+ * - Daily limit not reached
180
+ * - Cooldown period has passed
181
+ *
182
+ * @returns True if suggestions are allowed
183
+ *
184
+ * @example
185
+ * if (engine.canSuggest()) {
186
+ * const suggestions = engine.getSuggestions(context)
187
+ * // Show suggestion to user
188
+ * }
189
+ */
190
+ canSuggest() {
191
+ if (this.state.optedOut)
192
+ return false;
193
+ if (this.state.suggestionsToday >= this.config.maxSuggestionsPerDay)
194
+ return false;
195
+ const timeSinceLastSuggestion = Date.now() - this.state.lastSuggestionTime;
196
+ return timeSinceLastSuggestion >= this.config.cooldownMs;
197
+ }
198
+ /**
199
+ * Get skill suggestions based on project context
200
+ *
201
+ * Returns empty array if rate limited or opted out.
202
+ * Filters out already installed and dismissed skills.
203
+ * Returns at most one suggestion (the highest priority match).
204
+ *
205
+ * @param context - Detected project context from project-detector
206
+ * @param installedSkills - Array of currently installed skill IDs
207
+ * @returns Array of skill suggestions (at most one)
208
+ *
209
+ * @example
210
+ * const context = detectProjectContext()
211
+ * const suggestions = engine.getSuggestions(context, ['user/docker'])
212
+ *
213
+ * if (suggestions.length > 0) {
214
+ * console.log(`Try: ${suggestions[0].skillName}`)
215
+ * }
216
+ */
217
+ getSuggestions(context, installedSkills = []) {
218
+ if (!this.canSuggest())
219
+ return [];
220
+ const suggestions = [];
221
+ for (const rule of SUGGESTION_RULES) {
222
+ // Skip if already installed
223
+ if (installedSkills.some((s) => s.includes(rule.skillName)))
224
+ continue;
225
+ // Skip if dismissed
226
+ if (this.state.dismissedSkills.includes(rule.skillId))
227
+ continue;
228
+ // Check condition
229
+ if (rule.condition(context)) {
230
+ suggestions.push({
231
+ skillId: rule.skillId,
232
+ skillName: rule.skillName,
233
+ reason: rule.reason,
234
+ priority: rule.priority,
235
+ contextMatch: this.getContextMatches(context),
236
+ });
237
+ }
238
+ }
239
+ // Sort by priority and return top suggestion
240
+ return suggestions.sort((a, b) => a.priority - b.priority).slice(0, 1);
241
+ }
242
+ /**
243
+ * Get list of context attributes that are true
244
+ *
245
+ * @param context - Project context to analyze
246
+ * @returns Array of context match strings
247
+ */
248
+ getContextMatches(context) {
249
+ const matches = [];
250
+ if (context.hasDocker)
251
+ matches.push('hasDocker');
252
+ if (context.hasLinear)
253
+ matches.push('hasLinear');
254
+ if (context.hasGitHub)
255
+ matches.push('hasGitHub');
256
+ if (context.testFramework)
257
+ matches.push(`testFramework:${context.testFramework}`);
258
+ if (context.apiFramework)
259
+ matches.push(`apiFramework:${context.apiFramework}`);
260
+ if (context.hasNativeModules)
261
+ matches.push('hasNativeModules');
262
+ return matches;
263
+ }
264
+ /**
265
+ * Record that a suggestion was shown to the user
266
+ *
267
+ * Updates the last suggestion time and increments daily counter.
268
+ * Should be called after displaying a suggestion.
269
+ *
270
+ * @example
271
+ * const suggestions = engine.getSuggestions(context)
272
+ * if (suggestions.length > 0) {
273
+ * displaySuggestion(suggestions[0])
274
+ * engine.recordSuggestionShown()
275
+ * }
276
+ */
277
+ recordSuggestionShown() {
278
+ this.state.lastSuggestionTime = Date.now();
279
+ this.state.suggestionsToday++;
280
+ this.saveState();
281
+ }
282
+ /**
283
+ * Dismiss a skill so it won't be suggested again
284
+ *
285
+ * User can dismiss skills they're not interested in.
286
+ *
287
+ * @param skillId - Full skill identifier to dismiss
288
+ *
289
+ * @example
290
+ * // User clicks "Don't show again" on docker suggestion
291
+ * engine.dismissSkill('community/docker')
292
+ */
293
+ dismissSkill(skillId) {
294
+ if (!this.state.dismissedSkills.includes(skillId)) {
295
+ this.state.dismissedSkills.push(skillId);
296
+ this.saveState();
297
+ }
298
+ }
299
+ /**
300
+ * Permanently opt out of all suggestions
301
+ *
302
+ * User can disable all suggestions. Use optIn() to reverse.
303
+ *
304
+ * @example
305
+ * // User clicks "Never show suggestions"
306
+ * engine.optOut()
307
+ */
308
+ optOut() {
309
+ this.state.optedOut = true;
310
+ this.saveState();
311
+ }
312
+ /**
313
+ * Opt back in to suggestions after opting out
314
+ *
315
+ * @example
316
+ * // User re-enables suggestions in settings
317
+ * engine.optIn()
318
+ */
319
+ optIn() {
320
+ this.state.optedOut = false;
321
+ this.saveState();
322
+ }
323
+ /**
324
+ * Reset all suggestion state to defaults
325
+ *
326
+ * Clears dismissed skills, resets counters, and re-enables suggestions.
327
+ *
328
+ * @example
329
+ * // User clicks "Reset suggestions"
330
+ * engine.resetState()
331
+ */
332
+ resetState() {
333
+ this.state = this.getDefaultState();
334
+ this.saveState();
335
+ }
336
+ /**
337
+ * Get a deep copy of the current suggestion state
338
+ *
339
+ * @returns Deep copy of current state (modifications don't affect engine)
340
+ *
341
+ * @example
342
+ * const state = engine.getState()
343
+ * console.log(`Suggestions today: ${state.suggestionsToday}`)
344
+ */
345
+ getState() {
346
+ return {
347
+ ...this.state,
348
+ dismissedSkills: [...this.state.dismissedSkills],
349
+ };
350
+ }
351
+ }
352
+ //# sourceMappingURL=suggestion-engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"suggestion-engine.js","sourceRoot":"","sources":["../../../src/suggestions/suggestion-engine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * @fileoverview Type definitions for Contextual Skill Suggestions
3
+ * @module @skillsmith/mcp-server/suggestions/types
4
+ * @see SMI-913: Contextual skill suggestions after first success
5
+ *
6
+ * Defines interfaces for the suggestion engine that recommends relevant Tier 2 skills
7
+ * after a user successfully uses a Tier 1 skill, based on project context.
8
+ *
9
+ * @example
10
+ * import type { SkillSuggestion, SuggestionConfig, SuggestionState } from './types.js'
11
+ *
12
+ * const suggestion: SkillSuggestion = {
13
+ * skillId: 'community/docker',
14
+ * skillName: 'docker',
15
+ * reason: 'Your project uses native modules - Docker ensures consistent builds',
16
+ * priority: 1,
17
+ * contextMatch: ['hasDocker', 'hasNativeModules'],
18
+ * }
19
+ */
20
+ /**
21
+ * A skill suggestion generated based on project context
22
+ *
23
+ * @example
24
+ * const suggestion: SkillSuggestion = {
25
+ * skillId: 'community/docker',
26
+ * skillName: 'docker',
27
+ * reason: 'Your project uses native modules - Docker ensures consistent builds',
28
+ * priority: 1,
29
+ * contextMatch: ['hasDocker', 'hasNativeModules'],
30
+ * }
31
+ */
32
+ export interface SkillSuggestion {
33
+ /** Full skill identifier (e.g., 'community/docker', 'user/linear') */
34
+ skillId: string;
35
+ /** Short skill name for display (e.g., 'docker', 'linear') */
36
+ skillName: string;
37
+ /** Human-readable reason why this skill is being suggested */
38
+ reason: string;
39
+ /** Priority level (1 = highest priority, higher numbers = lower priority) */
40
+ priority: number;
41
+ /** Context attributes that matched this suggestion (e.g., ['hasDocker', 'testFramework:jest']) */
42
+ contextMatch: string[];
43
+ }
44
+ /**
45
+ * Configuration options for the suggestion engine
46
+ *
47
+ * @example
48
+ * const config: SuggestionConfig = {
49
+ * cooldownMs: 5 * 60 * 1000, // 5 minutes
50
+ * maxSuggestionsPerDay: 3,
51
+ * enableOptOut: true,
52
+ * }
53
+ */
54
+ export interface SuggestionConfig {
55
+ /** Minimum time between suggestions in milliseconds (default: 5 minutes) */
56
+ cooldownMs: number;
57
+ /** Maximum number of suggestions per day (default: 3) */
58
+ maxSuggestionsPerDay: number;
59
+ /** Whether to allow permanent opt-out from suggestions (default: true) */
60
+ enableOptOut: boolean;
61
+ /** Custom state directory path (default: ~/.skillsmith) - primarily for testing */
62
+ stateDir?: string;
63
+ }
64
+ /**
65
+ * Persistent state for the suggestion engine
66
+ *
67
+ * This state is persisted to disk in ~/.skillsmith/suggestions-state.json
68
+ * and tracks suggestion history, rate limiting, and user preferences.
69
+ *
70
+ * @example
71
+ * const state: SuggestionState = {
72
+ * lastSuggestionTime: Date.now(),
73
+ * suggestionsToday: 1,
74
+ * optedOut: false,
75
+ * dismissedSkills: ['community/docker'],
76
+ * }
77
+ */
78
+ export interface SuggestionState {
79
+ /** Timestamp of the last suggestion shown (for cooldown calculation) */
80
+ lastSuggestionTime: number;
81
+ /** Number of suggestions shown today (resets at midnight) */
82
+ suggestionsToday: number;
83
+ /** Whether the user has permanently opted out of suggestions */
84
+ optedOut: boolean;
85
+ /** Array of skill IDs the user has dismissed (won't be suggested again) */
86
+ dismissedSkills: string[];
87
+ }
88
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/suggestions/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,eAAe;IAC9B,sEAAsE;IACtE,OAAO,EAAE,MAAM,CAAA;IAEf,8DAA8D;IAC9D,SAAS,EAAE,MAAM,CAAA;IAEjB,8DAA8D;IAC9D,MAAM,EAAE,MAAM,CAAA;IAEd,6EAA6E;IAC7E,QAAQ,EAAE,MAAM,CAAA;IAEhB,kGAAkG;IAClG,YAAY,EAAE,MAAM,EAAE,CAAA;CACvB;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,gBAAgB;IAC/B,4EAA4E;IAC5E,UAAU,EAAE,MAAM,CAAA;IAElB,yDAAyD;IACzD,oBAAoB,EAAE,MAAM,CAAA;IAE5B,0EAA0E;IAC1E,YAAY,EAAE,OAAO,CAAA;IAErB,mFAAmF;IACnF,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,eAAe;IAC9B,wEAAwE;IACxE,kBAAkB,EAAE,MAAM,CAAA;IAE1B,6DAA6D;IAC7D,gBAAgB,EAAE,MAAM,CAAA;IAExB,gEAAgE;IAChE,QAAQ,EAAE,OAAO,CAAA;IAEjB,2EAA2E;IAC3E,eAAe,EAAE,MAAM,EAAE,CAAA;CAC1B"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @fileoverview Type definitions for Contextual Skill Suggestions
3
+ * @module @skillsmith/mcp-server/suggestions/types
4
+ * @see SMI-913: Contextual skill suggestions after first success
5
+ *
6
+ * Defines interfaces for the suggestion engine that recommends relevant Tier 2 skills
7
+ * after a user successfully uses a Tier 1 skill, based on project context.
8
+ *
9
+ * @example
10
+ * import type { SkillSuggestion, SuggestionConfig, SuggestionState } from './types.js'
11
+ *
12
+ * const suggestion: SkillSuggestion = {
13
+ * skillId: 'community/docker',
14
+ * skillName: 'docker',
15
+ * reason: 'Your project uses native modules - Docker ensures consistent builds',
16
+ * priority: 1,
17
+ * contextMatch: ['hasDocker', 'hasNativeModules'],
18
+ * }
19
+ */
20
+ export {};
21
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/suggestions/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG"}
@@ -0,0 +1,151 @@
1
+ /**
2
+ * @fileoverview MCP analyze_codebase Tool
3
+ * @module @skillsmith/mcp-server/tools/analyze
4
+ * @see SMI-600: Implement analyze_codebase MCP tool
5
+ *
6
+ * Analyzes a codebase to extract context for skill recommendations.
7
+ * Uses TypeScript/JavaScript analysis per ADR-010.
8
+ *
9
+ * @example
10
+ * // Analyze current directory
11
+ * const result = await executeAnalyze({ path: '.' });
12
+ * console.log(result.frameworks);
13
+ *
14
+ * @example
15
+ * // Analyze with options
16
+ * const result = await executeAnalyze({
17
+ * path: '/path/to/project',
18
+ * max_files: 500,
19
+ * include_dev_deps: false
20
+ * });
21
+ */
22
+ import { z } from 'zod';
23
+ /**
24
+ * Zod schema for analyze tool input validation
25
+ */
26
+ export declare const analyzeInputSchema: z.ZodObject<{
27
+ /** Path to analyze (default: current directory) */
28
+ path: z.ZodDefault<z.ZodString>;
29
+ /** Maximum files to analyze (default: 1000) */
30
+ max_files: z.ZodDefault<z.ZodNumber>;
31
+ /** Directories to exclude */
32
+ exclude_dirs: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
33
+ /** Include dev dependencies in analysis */
34
+ include_dev_deps: z.ZodDefault<z.ZodBoolean>;
35
+ }, "strip", z.ZodTypeAny, {
36
+ path: string;
37
+ max_files: number;
38
+ include_dev_deps: boolean;
39
+ exclude_dirs?: string[] | undefined;
40
+ }, {
41
+ path?: string | undefined;
42
+ max_files?: number | undefined;
43
+ exclude_dirs?: string[] | undefined;
44
+ include_dev_deps?: boolean | undefined;
45
+ }>;
46
+ /**
47
+ * Input type for analyze tool
48
+ */
49
+ export type AnalyzeInput = z.input<typeof analyzeInputSchema>;
50
+ /**
51
+ * Simplified framework info for response
52
+ */
53
+ export interface AnalyzeFramework {
54
+ /** Framework name */
55
+ name: string;
56
+ /** Confidence level (0-100) */
57
+ confidence: number;
58
+ }
59
+ /**
60
+ * Simplified dependency info for response
61
+ */
62
+ export interface AnalyzeDependency {
63
+ /** Package name */
64
+ name: string;
65
+ /** Whether this is a dev dependency */
66
+ is_dev: boolean;
67
+ }
68
+ /**
69
+ * Analysis response with codebase context
70
+ */
71
+ export interface AnalyzeResponse {
72
+ /** Detected frameworks */
73
+ frameworks: AnalyzeFramework[];
74
+ /** Top dependencies */
75
+ dependencies: AnalyzeDependency[];
76
+ /** Unique import modules */
77
+ imports: string[];
78
+ /** File statistics */
79
+ stats: {
80
+ total_files: number;
81
+ total_lines: number;
82
+ file_types: Record<string, number>;
83
+ };
84
+ /** Summary for skill matching */
85
+ summary: string;
86
+ /** Analysis timing */
87
+ timing: {
88
+ duration_ms: number;
89
+ };
90
+ }
91
+ /**
92
+ * MCP tool schema definition for analyze_codebase
93
+ */
94
+ export declare const analyzeToolSchema: {
95
+ name: string;
96
+ description: string;
97
+ inputSchema: {
98
+ type: "object";
99
+ properties: {
100
+ path: {
101
+ type: string;
102
+ description: string;
103
+ default: string;
104
+ };
105
+ max_files: {
106
+ type: string;
107
+ description: string;
108
+ minimum: number;
109
+ maximum: number;
110
+ default: number;
111
+ };
112
+ exclude_dirs: {
113
+ type: string;
114
+ items: {
115
+ type: string;
116
+ };
117
+ description: string;
118
+ };
119
+ include_dev_deps: {
120
+ type: string;
121
+ description: string;
122
+ default: boolean;
123
+ };
124
+ };
125
+ required: never[];
126
+ };
127
+ };
128
+ /**
129
+ * Execute codebase analysis.
130
+ *
131
+ * Scans the specified directory for TypeScript/JavaScript files,
132
+ * extracts imports, detects frameworks, and returns context
133
+ * suitable for skill recommendations.
134
+ *
135
+ * @param input - Analysis parameters
136
+ * @returns Promise resolving to analysis response
137
+ * @throws {Error} When path doesn't exist or analysis fails
138
+ *
139
+ * @example
140
+ * const response = await executeAnalyze({
141
+ * path: './my-project',
142
+ * max_files: 500
143
+ * });
144
+ * console.log('Detected:', response.frameworks.map(f => f.name).join(', '));
145
+ */
146
+ export declare function executeAnalyze(input: AnalyzeInput): Promise<AnalyzeResponse>;
147
+ /**
148
+ * Format analysis results for terminal display
149
+ */
150
+ export declare function formatAnalysisResults(response: AnalyzeResponse): string;
151
+ //# sourceMappingURL=analyze.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../../../src/tools/analyze.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB;;GAEG;AACH,eAAO,MAAM,kBAAkB;IAC7B,mDAAmD;;IAEnD,+CAA+C;;IAE/C,6BAA6B;;IAE7B,2CAA2C;;;;;;;;;;;;EAE3C,CAAA;AAEF;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAA;AAE7D;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,qBAAqB;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ,+BAA+B;IAC/B,UAAU,EAAE,MAAM,CAAA;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAA;IACZ,uCAAuC;IACvC,MAAM,EAAE,OAAO,CAAA;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,0BAA0B;IAC1B,UAAU,EAAE,gBAAgB,EAAE,CAAA;IAC9B,uBAAuB;IACvB,YAAY,EAAE,iBAAiB,EAAE,CAAA;IACjC,4BAA4B;IAC5B,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,sBAAsB;IACtB,KAAK,EAAE;QACL,WAAW,EAAE,MAAM,CAAA;QACnB,WAAW,EAAE,MAAM,CAAA;QACnB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KACnC,CAAA;IACD,iCAAiC;IACjC,OAAO,EAAE,MAAM,CAAA;IACf,sBAAsB;IACtB,MAAM,EAAE;QACN,WAAW,EAAE,MAAM,CAAA;KACpB,CAAA;CACF;AAED;;GAEG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkC7B,CAAA;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,cAAc,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,CAAC,CAclF;AAsDD;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,eAAe,GAAG,MAAM,CAoEvE"}