convex 1.34.1 → 1.35.1

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 (453) hide show
  1. package/CHANGELOG.md +86 -43
  2. package/dist/browser.bundle.js +1 -1
  3. package/dist/browser.bundle.js.map +1 -1
  4. package/dist/cjs/cli/aiFiles.js +31 -13
  5. package/dist/cjs/cli/aiFiles.js.map +3 -3
  6. package/dist/cjs/cli/configure.js +21 -24
  7. package/dist/cjs/cli/configure.js.map +2 -2
  8. package/dist/cjs/cli/deploy.js +8 -9
  9. package/dist/cjs/cli/deploy.js.map +2 -2
  10. package/dist/cjs/cli/deploymentCreate.js +225 -40
  11. package/dist/cjs/cli/deploymentCreate.js.map +3 -3
  12. package/dist/cjs/cli/deploymentSelect.js +14 -13
  13. package/dist/cjs/cli/deploymentSelect.js.map +2 -2
  14. package/dist/cjs/cli/dev.js +30 -11
  15. package/dist/cjs/cli/dev.js.map +2 -2
  16. package/dist/cjs/cli/docs.js +1 -1
  17. package/dist/cjs/cli/docs.js.map +2 -2
  18. package/dist/cjs/cli/init.js +1 -1
  19. package/dist/cjs/cli/init.js.map +2 -2
  20. package/dist/cjs/cli/lib/aiFiles/agentsmd.js +14 -10
  21. package/dist/cjs/cli/lib/aiFiles/agentsmd.js.map +2 -2
  22. package/dist/cjs/cli/lib/aiFiles/claudemd.js +14 -10
  23. package/dist/cjs/cli/lib/aiFiles/claudemd.js.map +2 -2
  24. package/dist/cjs/cli/lib/aiFiles/guidelinesmd.js +10 -3
  25. package/dist/cjs/cli/lib/aiFiles/guidelinesmd.js.map +2 -2
  26. package/dist/cjs/cli/lib/aiFiles/index.js +70 -86
  27. package/dist/cjs/cli/lib/aiFiles/index.js.map +3 -3
  28. package/dist/cjs/cli/lib/aiFiles/skills.js +28 -12
  29. package/dist/cjs/cli/lib/aiFiles/skills.js.map +2 -2
  30. package/dist/cjs/cli/lib/aiFiles/state.js +96 -0
  31. package/dist/cjs/cli/lib/aiFiles/state.js.map +7 -0
  32. package/dist/cjs/cli/lib/aiFiles/status.js +31 -28
  33. package/dist/cjs/cli/lib/aiFiles/status.js.map +2 -2
  34. package/dist/cjs/cli/lib/aiFiles/utils.js +31 -14
  35. package/dist/cjs/cli/lib/aiFiles/utils.js.map +2 -2
  36. package/dist/cjs/cli/lib/api.js +70 -7
  37. package/dist/cjs/cli/lib/api.js.map +2 -2
  38. package/dist/cjs/cli/lib/command.js +4 -5
  39. package/dist/cjs/cli/lib/command.js.map +2 -2
  40. package/dist/cjs/cli/lib/config.js +41 -4
  41. package/dist/cjs/cli/lib/config.js.map +3 -3
  42. package/dist/cjs/cli/lib/deploy2.js +9 -26
  43. package/dist/cjs/cli/lib/deploy2.js.map +2 -2
  44. package/dist/cjs/cli/lib/deployApi/componentDefinition.js +4 -1
  45. package/dist/cjs/cli/lib/deployApi/componentDefinition.js.map +2 -2
  46. package/dist/cjs/cli/lib/deploymentSelection.js +45 -2
  47. package/dist/cjs/cli/lib/deploymentSelection.js.map +2 -2
  48. package/dist/cjs/cli/lib/deploymentSelector.js +1 -0
  49. package/dist/cjs/cli/lib/deploymentSelector.js.map +2 -2
  50. package/dist/cjs/cli/lib/dev.js +162 -117
  51. package/dist/cjs/cli/lib/dev.js.map +2 -2
  52. package/dist/cjs/cli/lib/env.js +1 -13
  53. package/dist/cjs/cli/lib/env.js.map +2 -2
  54. package/dist/cjs/cli/lib/envvars.js +8 -1
  55. package/dist/cjs/cli/lib/envvars.js.map +2 -2
  56. package/dist/cjs/cli/lib/expiration.js +104 -0
  57. package/dist/cjs/cli/lib/expiration.js.map +7 -0
  58. package/dist/cjs/cli/lib/generatedFunctionLogsApi.js.map +1 -1
  59. package/dist/cjs/cli/lib/init.js +4 -3
  60. package/dist/cjs/cli/lib/init.js.map +2 -2
  61. package/dist/cjs/cli/lib/insights.js +1 -1
  62. package/dist/cjs/cli/lib/insights.js.map +2 -2
  63. package/dist/cjs/cli/lib/localDeployment/anonymous.js +14 -7
  64. package/dist/cjs/cli/lib/localDeployment/anonymous.js.map +2 -2
  65. package/dist/cjs/cli/lib/localDeployment/localDeployment.js +8 -10
  66. package/dist/cjs/cli/lib/localDeployment/localDeployment.js.map +2 -2
  67. package/dist/cjs/cli/lib/localDeployment/run.js +1 -0
  68. package/dist/cjs/cli/lib/localDeployment/run.js.map +2 -2
  69. package/dist/cjs/cli/lib/localDeployment/upgrade.js +2 -2
  70. package/dist/cjs/cli/lib/localDeployment/upgrade.js.map +2 -2
  71. package/dist/cjs/cli/lib/localDeployment/utils.js +9 -0
  72. package/dist/cjs/cli/lib/localDeployment/utils.js.map +2 -2
  73. package/dist/cjs/cli/lib/mcp/tools/status.js +1 -1
  74. package/dist/cjs/cli/lib/mcp/tools/status.js.map +2 -2
  75. package/dist/cjs/cli/lib/updates.js +8 -9
  76. package/dist/cjs/cli/lib/updates.js.map +2 -2
  77. package/dist/cjs/cli/lib/usage.js +2 -1
  78. package/dist/cjs/cli/lib/usage.js.map +2 -2
  79. package/dist/cjs/cli/lib/utils/prompts.js +2 -1
  80. package/dist/cjs/cli/lib/utils/prompts.js.map +2 -2
  81. package/dist/cjs/cli/lib/utils/utils.js +46 -20
  82. package/dist/cjs/cli/lib/utils/utils.js.map +3 -3
  83. package/dist/cjs/index.js +1 -1
  84. package/dist/cjs/index.js.map +1 -1
  85. package/dist/cjs/react-clerk/ConvexProviderWithClerk.js.map +1 -1
  86. package/dist/cjs/server/components/definition.js.map +1 -1
  87. package/dist/cjs/server/components/index.js +40 -4
  88. package/dist/cjs/server/components/index.js.map +2 -2
  89. package/dist/cjs/server/data_model.js.map +1 -1
  90. package/dist/cjs/server/impl/meta_impl.js +78 -0
  91. package/dist/cjs/server/impl/meta_impl.js.map +7 -0
  92. package/dist/cjs/server/impl/registration_impl.js +16 -11
  93. package/dist/cjs/server/impl/registration_impl.js.map +2 -2
  94. package/dist/cjs/server/index.js.map +2 -2
  95. package/dist/cjs/server/meta.js +17 -0
  96. package/dist/cjs/server/meta.js.map +7 -0
  97. package/dist/cjs/server/registration.js.map +1 -1
  98. package/dist/cjs-types/cli/aiFiles.d.ts.map +1 -1
  99. package/dist/cjs-types/cli/configure.d.ts.map +1 -1
  100. package/dist/cjs-types/cli/configure.test.d.ts +2 -0
  101. package/dist/cjs-types/cli/configure.test.d.ts.map +1 -0
  102. package/dist/cjs-types/cli/deploy.d.ts.map +1 -1
  103. package/dist/cjs-types/cli/deploymentCreate.d.ts +1 -0
  104. package/dist/cjs-types/cli/deploymentCreate.d.ts.map +1 -1
  105. package/dist/cjs-types/cli/deploymentSelect.d.ts +2 -1
  106. package/dist/cjs-types/cli/deploymentSelect.d.ts.map +1 -1
  107. package/dist/cjs-types/cli/dev.d.ts +3 -1
  108. package/dist/cjs-types/cli/dev.d.ts.map +1 -1
  109. package/dist/cjs-types/cli/lib/aiFiles/agentsmd.d.ts +5 -5
  110. package/dist/cjs-types/cli/lib/aiFiles/agentsmd.d.ts.map +1 -1
  111. package/dist/cjs-types/cli/lib/aiFiles/claudemd.d.ts +5 -5
  112. package/dist/cjs-types/cli/lib/aiFiles/claudemd.d.ts.map +1 -1
  113. package/dist/cjs-types/cli/lib/aiFiles/guidelinesmd.d.ts +3 -3
  114. package/dist/cjs-types/cli/lib/aiFiles/guidelinesmd.d.ts.map +1 -1
  115. package/dist/cjs-types/cli/lib/aiFiles/index.d.ts +20 -18
  116. package/dist/cjs-types/cli/lib/aiFiles/index.d.ts.map +1 -1
  117. package/dist/cjs-types/cli/lib/aiFiles/skills.d.ts +6 -4
  118. package/dist/cjs-types/cli/lib/aiFiles/skills.d.ts.map +1 -1
  119. package/dist/cjs-types/cli/lib/aiFiles/state.d.ts +38 -0
  120. package/dist/cjs-types/cli/lib/aiFiles/state.d.ts.map +1 -0
  121. package/dist/cjs-types/cli/lib/aiFiles/state.test.d.ts +2 -0
  122. package/dist/cjs-types/cli/lib/aiFiles/state.test.d.ts.map +1 -0
  123. package/dist/cjs-types/cli/lib/aiFiles/status.d.ts +4 -1
  124. package/dist/cjs-types/cli/lib/aiFiles/status.d.ts.map +1 -1
  125. package/dist/cjs-types/cli/lib/aiFiles/utils.d.ts +13 -3
  126. package/dist/cjs-types/cli/lib/aiFiles/utils.d.ts.map +1 -1
  127. package/dist/cjs-types/cli/lib/aiFiles/utils.test.d.ts +2 -0
  128. package/dist/cjs-types/cli/lib/aiFiles/utils.test.d.ts.map +1 -0
  129. package/dist/cjs-types/cli/lib/api.d.ts +3 -3
  130. package/dist/cjs-types/cli/lib/api.d.ts.map +1 -1
  131. package/dist/cjs-types/cli/lib/command.d.ts +2 -1
  132. package/dist/cjs-types/cli/lib/command.d.ts.map +1 -1
  133. package/dist/cjs-types/cli/lib/config.d.ts +17 -6
  134. package/dist/cjs-types/cli/lib/config.d.ts.map +1 -1
  135. package/dist/cjs-types/cli/lib/deploy2.d.ts +5 -2
  136. package/dist/cjs-types/cli/lib/deploy2.d.ts.map +1 -1
  137. package/dist/cjs-types/cli/lib/deployApi/componentDefinition.d.ts +27 -12
  138. package/dist/cjs-types/cli/lib/deployApi/componentDefinition.d.ts.map +1 -1
  139. package/dist/cjs-types/cli/lib/deployApi/modules.d.ts +12 -12
  140. package/dist/cjs-types/cli/lib/deployApi/startPush.d.ts +25 -16
  141. package/dist/cjs-types/cli/lib/deployApi/startPush.d.ts.map +1 -1
  142. package/dist/cjs-types/cli/lib/deploymentSelection.d.ts.map +1 -1
  143. package/dist/cjs-types/cli/lib/deploymentSelector.d.ts +2 -0
  144. package/dist/cjs-types/cli/lib/deploymentSelector.d.ts.map +1 -1
  145. package/dist/cjs-types/cli/lib/dev.d.ts.map +1 -1
  146. package/dist/cjs-types/cli/lib/env.d.ts +0 -4
  147. package/dist/cjs-types/cli/lib/env.d.ts.map +1 -1
  148. package/dist/cjs-types/cli/lib/envvars.d.ts.map +1 -1
  149. package/dist/cjs-types/cli/lib/expiration.d.ts +35 -0
  150. package/dist/cjs-types/cli/lib/expiration.d.ts.map +1 -0
  151. package/dist/cjs-types/cli/lib/expiration.test.d.ts +2 -0
  152. package/dist/cjs-types/cli/lib/expiration.test.d.ts.map +1 -0
  153. package/dist/cjs-types/cli/lib/generatedFunctionLogsApi.d.ts +16 -1
  154. package/dist/cjs-types/cli/lib/generatedFunctionLogsApi.d.ts.map +1 -1
  155. package/dist/cjs-types/cli/lib/init.d.ts.map +1 -1
  156. package/dist/cjs-types/cli/lib/localDeployment/anonymous.d.ts.map +1 -1
  157. package/dist/cjs-types/cli/lib/localDeployment/localDeployment.d.ts.map +1 -1
  158. package/dist/cjs-types/cli/lib/localDeployment/run.d.ts +15 -0
  159. package/dist/cjs-types/cli/lib/localDeployment/run.d.ts.map +1 -1
  160. package/dist/cjs-types/cli/lib/localDeployment/upgrade.d.ts.map +1 -1
  161. package/dist/cjs-types/cli/lib/localDeployment/utils.d.ts +7 -0
  162. package/dist/cjs-types/cli/lib/localDeployment/utils.d.ts.map +1 -1
  163. package/dist/cjs-types/cli/lib/mcp/requestContext.d.ts +3 -3
  164. package/dist/cjs-types/cli/lib/updates.d.ts +4 -3
  165. package/dist/cjs-types/cli/lib/updates.d.ts.map +1 -1
  166. package/dist/cjs-types/cli/lib/usage.d.ts.map +1 -1
  167. package/dist/cjs-types/cli/lib/utils/prompts.d.ts +1 -0
  168. package/dist/cjs-types/cli/lib/utils/prompts.d.ts.map +1 -1
  169. package/dist/cjs-types/cli/lib/utils/utils.d.ts +16 -2
  170. package/dist/cjs-types/cli/lib/utils/utils.d.ts.map +1 -1
  171. package/dist/cjs-types/index.d.ts +1 -1
  172. package/dist/cjs-types/react-clerk/ConvexProviderWithClerk.d.ts +1 -1
  173. package/dist/cjs-types/server/components/definition.d.ts +1 -0
  174. package/dist/cjs-types/server/components/definition.d.ts.map +1 -1
  175. package/dist/cjs-types/server/components/index.d.ts +5 -1
  176. package/dist/cjs-types/server/components/index.d.ts.map +1 -1
  177. package/dist/cjs-types/server/data_model.d.ts +2 -1
  178. package/dist/cjs-types/server/data_model.d.ts.map +1 -1
  179. package/dist/cjs-types/server/impl/meta_impl.d.ts +5 -0
  180. package/dist/cjs-types/server/impl/meta_impl.d.ts.map +1 -0
  181. package/dist/cjs-types/server/impl/registration_impl.d.ts.map +1 -1
  182. package/dist/cjs-types/server/index.d.ts +1 -0
  183. package/dist/cjs-types/server/index.d.ts.map +1 -1
  184. package/dist/cjs-types/server/meta.d.ts +72 -0
  185. package/dist/cjs-types/server/meta.d.ts.map +1 -0
  186. package/dist/cjs-types/server/registration.d.ts.map +1 -1
  187. package/dist/cli.bundle.cjs +1665 -1215
  188. package/dist/cli.bundle.cjs.map +4 -4
  189. package/dist/esm/cli/aiFiles.js +33 -15
  190. package/dist/esm/cli/aiFiles.js.map +2 -2
  191. package/dist/esm/cli/configure.js +23 -26
  192. package/dist/esm/cli/configure.js.map +2 -2
  193. package/dist/esm/cli/deploy.js +12 -11
  194. package/dist/esm/cli/deploy.js.map +2 -2
  195. package/dist/esm/cli/deploymentCreate.js +238 -42
  196. package/dist/esm/cli/deploymentCreate.js.map +2 -2
  197. package/dist/esm/cli/deploymentSelect.js +13 -12
  198. package/dist/esm/cli/deploymentSelect.js.map +2 -2
  199. package/dist/esm/cli/dev.js +34 -13
  200. package/dist/esm/cli/dev.js.map +2 -2
  201. package/dist/esm/cli/docs.js +1 -1
  202. package/dist/esm/cli/docs.js.map +2 -2
  203. package/dist/esm/cli/init.js +2 -2
  204. package/dist/esm/cli/init.js.map +2 -2
  205. package/dist/esm/cli/lib/aiFiles/agentsmd.js +13 -9
  206. package/dist/esm/cli/lib/aiFiles/agentsmd.js.map +2 -2
  207. package/dist/esm/cli/lib/aiFiles/claudemd.js +13 -9
  208. package/dist/esm/cli/lib/aiFiles/claudemd.js.map +2 -2
  209. package/dist/esm/cli/lib/aiFiles/guidelinesmd.js +12 -5
  210. package/dist/esm/cli/lib/aiFiles/guidelinesmd.js.map +2 -2
  211. package/dist/esm/cli/lib/aiFiles/index.js +72 -89
  212. package/dist/esm/cli/lib/aiFiles/index.js.map +2 -2
  213. package/dist/esm/cli/lib/aiFiles/skills.js +29 -13
  214. package/dist/esm/cli/lib/aiFiles/skills.js.map +2 -2
  215. package/dist/esm/cli/lib/aiFiles/state.js +60 -0
  216. package/dist/esm/cli/lib/aiFiles/state.js.map +7 -0
  217. package/dist/esm/cli/lib/aiFiles/status.js +32 -29
  218. package/dist/esm/cli/lib/aiFiles/status.js.map +2 -2
  219. package/dist/esm/cli/lib/aiFiles/utils.js +25 -10
  220. package/dist/esm/cli/lib/aiFiles/utils.js.map +2 -2
  221. package/dist/esm/cli/lib/api.js +70 -7
  222. package/dist/esm/cli/lib/api.js.map +2 -2
  223. package/dist/esm/cli/lib/command.js +4 -5
  224. package/dist/esm/cli/lib/command.js.map +2 -2
  225. package/dist/esm/cli/lib/config.js +39 -3
  226. package/dist/esm/cli/lib/config.js.map +2 -2
  227. package/dist/esm/cli/lib/deploy2.js +13 -26
  228. package/dist/esm/cli/lib/deploy2.js.map +2 -2
  229. package/dist/esm/cli/lib/deployApi/componentDefinition.js +4 -1
  230. package/dist/esm/cli/lib/deployApi/componentDefinition.js.map +2 -2
  231. package/dist/esm/cli/lib/deploymentSelection.js +46 -2
  232. package/dist/esm/cli/lib/deploymentSelection.js.map +2 -2
  233. package/dist/esm/cli/lib/deploymentSelector.js +1 -0
  234. package/dist/esm/cli/lib/deploymentSelector.js.map +2 -2
  235. package/dist/esm/cli/lib/dev.js +162 -118
  236. package/dist/esm/cli/lib/dev.js.map +2 -2
  237. package/dist/esm/cli/lib/env.js +0 -11
  238. package/dist/esm/cli/lib/env.js.map +2 -2
  239. package/dist/esm/cli/lib/envvars.js +8 -1
  240. package/dist/esm/cli/lib/envvars.js.map +2 -2
  241. package/dist/esm/cli/lib/expiration.js +80 -0
  242. package/dist/esm/cli/lib/expiration.js.map +7 -0
  243. package/dist/esm/cli/lib/init.js +4 -3
  244. package/dist/esm/cli/lib/init.js.map +2 -2
  245. package/dist/esm/cli/lib/insights.js +1 -1
  246. package/dist/esm/cli/lib/insights.js.map +2 -2
  247. package/dist/esm/cli/lib/localDeployment/anonymous.js +16 -9
  248. package/dist/esm/cli/lib/localDeployment/anonymous.js.map +2 -2
  249. package/dist/esm/cli/lib/localDeployment/localDeployment.js +9 -11
  250. package/dist/esm/cli/lib/localDeployment/localDeployment.js.map +2 -2
  251. package/dist/esm/cli/lib/localDeployment/run.js +1 -1
  252. package/dist/esm/cli/lib/localDeployment/run.js.map +2 -2
  253. package/dist/esm/cli/lib/localDeployment/upgrade.js +2 -2
  254. package/dist/esm/cli/lib/localDeployment/upgrade.js.map +2 -2
  255. package/dist/esm/cli/lib/localDeployment/utils.js +8 -0
  256. package/dist/esm/cli/lib/localDeployment/utils.js.map +2 -2
  257. package/dist/esm/cli/lib/mcp/tools/status.js +1 -1
  258. package/dist/esm/cli/lib/mcp/tools/status.js.map +2 -2
  259. package/dist/esm/cli/lib/updates.js +11 -9
  260. package/dist/esm/cli/lib/updates.js.map +2 -2
  261. package/dist/esm/cli/lib/usage.js +2 -1
  262. package/dist/esm/cli/lib/usage.js.map +2 -2
  263. package/dist/esm/cli/lib/utils/prompts.js +2 -1
  264. package/dist/esm/cli/lib/utils/prompts.js.map +2 -2
  265. package/dist/esm/cli/lib/utils/utils.js +45 -20
  266. package/dist/esm/cli/lib/utils/utils.js.map +3 -3
  267. package/dist/esm/index.js +1 -1
  268. package/dist/esm/index.js.map +1 -1
  269. package/dist/esm/react-clerk/ConvexProviderWithClerk.js.map +1 -1
  270. package/dist/esm/server/components/index.js +40 -4
  271. package/dist/esm/server/components/index.js.map +2 -2
  272. package/dist/esm/server/impl/meta_impl.js +54 -0
  273. package/dist/esm/server/impl/meta_impl.js.map +7 -0
  274. package/dist/esm/server/impl/registration_impl.js +20 -11
  275. package/dist/esm/server/impl/registration_impl.js.map +2 -2
  276. package/dist/esm/server/index.js.map +2 -2
  277. package/dist/esm/server/meta.js +2 -0
  278. package/dist/esm/server/meta.js.map +7 -0
  279. package/dist/esm-types/cli/aiFiles.d.ts.map +1 -1
  280. package/dist/esm-types/cli/configure.d.ts.map +1 -1
  281. package/dist/esm-types/cli/configure.test.d.ts +2 -0
  282. package/dist/esm-types/cli/configure.test.d.ts.map +1 -0
  283. package/dist/esm-types/cli/deploy.d.ts.map +1 -1
  284. package/dist/esm-types/cli/deploymentCreate.d.ts +1 -0
  285. package/dist/esm-types/cli/deploymentCreate.d.ts.map +1 -1
  286. package/dist/esm-types/cli/deploymentSelect.d.ts +2 -1
  287. package/dist/esm-types/cli/deploymentSelect.d.ts.map +1 -1
  288. package/dist/esm-types/cli/dev.d.ts +3 -1
  289. package/dist/esm-types/cli/dev.d.ts.map +1 -1
  290. package/dist/esm-types/cli/lib/aiFiles/agentsmd.d.ts +5 -5
  291. package/dist/esm-types/cli/lib/aiFiles/agentsmd.d.ts.map +1 -1
  292. package/dist/esm-types/cli/lib/aiFiles/claudemd.d.ts +5 -5
  293. package/dist/esm-types/cli/lib/aiFiles/claudemd.d.ts.map +1 -1
  294. package/dist/esm-types/cli/lib/aiFiles/guidelinesmd.d.ts +3 -3
  295. package/dist/esm-types/cli/lib/aiFiles/guidelinesmd.d.ts.map +1 -1
  296. package/dist/esm-types/cli/lib/aiFiles/index.d.ts +20 -18
  297. package/dist/esm-types/cli/lib/aiFiles/index.d.ts.map +1 -1
  298. package/dist/esm-types/cli/lib/aiFiles/skills.d.ts +6 -4
  299. package/dist/esm-types/cli/lib/aiFiles/skills.d.ts.map +1 -1
  300. package/dist/esm-types/cli/lib/aiFiles/state.d.ts +38 -0
  301. package/dist/esm-types/cli/lib/aiFiles/state.d.ts.map +1 -0
  302. package/dist/esm-types/cli/lib/aiFiles/state.test.d.ts +2 -0
  303. package/dist/esm-types/cli/lib/aiFiles/state.test.d.ts.map +1 -0
  304. package/dist/esm-types/cli/lib/aiFiles/status.d.ts +4 -1
  305. package/dist/esm-types/cli/lib/aiFiles/status.d.ts.map +1 -1
  306. package/dist/esm-types/cli/lib/aiFiles/utils.d.ts +13 -3
  307. package/dist/esm-types/cli/lib/aiFiles/utils.d.ts.map +1 -1
  308. package/dist/esm-types/cli/lib/aiFiles/utils.test.d.ts +2 -0
  309. package/dist/esm-types/cli/lib/aiFiles/utils.test.d.ts.map +1 -0
  310. package/dist/esm-types/cli/lib/api.d.ts +3 -3
  311. package/dist/esm-types/cli/lib/api.d.ts.map +1 -1
  312. package/dist/esm-types/cli/lib/command.d.ts +2 -1
  313. package/dist/esm-types/cli/lib/command.d.ts.map +1 -1
  314. package/dist/esm-types/cli/lib/config.d.ts +17 -6
  315. package/dist/esm-types/cli/lib/config.d.ts.map +1 -1
  316. package/dist/esm-types/cli/lib/deploy2.d.ts +5 -2
  317. package/dist/esm-types/cli/lib/deploy2.d.ts.map +1 -1
  318. package/dist/esm-types/cli/lib/deployApi/componentDefinition.d.ts +27 -12
  319. package/dist/esm-types/cli/lib/deployApi/componentDefinition.d.ts.map +1 -1
  320. package/dist/esm-types/cli/lib/deployApi/modules.d.ts +12 -12
  321. package/dist/esm-types/cli/lib/deployApi/startPush.d.ts +25 -16
  322. package/dist/esm-types/cli/lib/deployApi/startPush.d.ts.map +1 -1
  323. package/dist/esm-types/cli/lib/deploymentSelection.d.ts.map +1 -1
  324. package/dist/esm-types/cli/lib/deploymentSelector.d.ts +2 -0
  325. package/dist/esm-types/cli/lib/deploymentSelector.d.ts.map +1 -1
  326. package/dist/esm-types/cli/lib/dev.d.ts.map +1 -1
  327. package/dist/esm-types/cli/lib/env.d.ts +0 -4
  328. package/dist/esm-types/cli/lib/env.d.ts.map +1 -1
  329. package/dist/esm-types/cli/lib/envvars.d.ts.map +1 -1
  330. package/dist/esm-types/cli/lib/expiration.d.ts +35 -0
  331. package/dist/esm-types/cli/lib/expiration.d.ts.map +1 -0
  332. package/dist/esm-types/cli/lib/expiration.test.d.ts +2 -0
  333. package/dist/esm-types/cli/lib/expiration.test.d.ts.map +1 -0
  334. package/dist/esm-types/cli/lib/generatedFunctionLogsApi.d.ts +16 -1
  335. package/dist/esm-types/cli/lib/generatedFunctionLogsApi.d.ts.map +1 -1
  336. package/dist/esm-types/cli/lib/init.d.ts.map +1 -1
  337. package/dist/esm-types/cli/lib/localDeployment/anonymous.d.ts.map +1 -1
  338. package/dist/esm-types/cli/lib/localDeployment/localDeployment.d.ts.map +1 -1
  339. package/dist/esm-types/cli/lib/localDeployment/run.d.ts +15 -0
  340. package/dist/esm-types/cli/lib/localDeployment/run.d.ts.map +1 -1
  341. package/dist/esm-types/cli/lib/localDeployment/upgrade.d.ts.map +1 -1
  342. package/dist/esm-types/cli/lib/localDeployment/utils.d.ts +7 -0
  343. package/dist/esm-types/cli/lib/localDeployment/utils.d.ts.map +1 -1
  344. package/dist/esm-types/cli/lib/mcp/requestContext.d.ts +3 -3
  345. package/dist/esm-types/cli/lib/updates.d.ts +4 -3
  346. package/dist/esm-types/cli/lib/updates.d.ts.map +1 -1
  347. package/dist/esm-types/cli/lib/usage.d.ts.map +1 -1
  348. package/dist/esm-types/cli/lib/utils/prompts.d.ts +1 -0
  349. package/dist/esm-types/cli/lib/utils/prompts.d.ts.map +1 -1
  350. package/dist/esm-types/cli/lib/utils/utils.d.ts +16 -2
  351. package/dist/esm-types/cli/lib/utils/utils.d.ts.map +1 -1
  352. package/dist/esm-types/index.d.ts +1 -1
  353. package/dist/esm-types/react-clerk/ConvexProviderWithClerk.d.ts +1 -1
  354. package/dist/esm-types/server/components/definition.d.ts +1 -0
  355. package/dist/esm-types/server/components/definition.d.ts.map +1 -1
  356. package/dist/esm-types/server/components/index.d.ts +5 -1
  357. package/dist/esm-types/server/components/index.d.ts.map +1 -1
  358. package/dist/esm-types/server/data_model.d.ts +2 -1
  359. package/dist/esm-types/server/data_model.d.ts.map +1 -1
  360. package/dist/esm-types/server/impl/meta_impl.d.ts +5 -0
  361. package/dist/esm-types/server/impl/meta_impl.d.ts.map +1 -0
  362. package/dist/esm-types/server/impl/registration_impl.d.ts.map +1 -1
  363. package/dist/esm-types/server/index.d.ts +1 -0
  364. package/dist/esm-types/server/index.d.ts.map +1 -1
  365. package/dist/esm-types/server/meta.d.ts +72 -0
  366. package/dist/esm-types/server/meta.d.ts.map +1 -0
  367. package/dist/esm-types/server/registration.d.ts.map +1 -1
  368. package/dist/react.bundle.js +1 -1
  369. package/dist/react.bundle.js.map +1 -1
  370. package/package.json +11 -7
  371. package/schemas/convex.schema.json +15 -2
  372. package/src/cli/aiFiles.ts +44 -14
  373. package/src/cli/configure.test.ts +138 -0
  374. package/src/cli/configure.ts +48 -47
  375. package/src/cli/deploy.ts +13 -10
  376. package/src/cli/deploymentCreate.test.ts +349 -14
  377. package/src/cli/deploymentCreate.ts +268 -41
  378. package/src/cli/deploymentSelect.test.ts +136 -27
  379. package/src/cli/deploymentSelect.ts +50 -41
  380. package/src/cli/deploymentSelection.test.ts +343 -35
  381. package/src/cli/dev.ts +49 -14
  382. package/src/cli/docs.ts +1 -1
  383. package/src/cli/init.ts +2 -2
  384. package/src/cli/lib/aiFiles/agentsmd.ts +15 -11
  385. package/src/cli/lib/aiFiles/claudemd.ts +15 -11
  386. package/src/cli/lib/aiFiles/guidelinesmd.test.ts +12 -2
  387. package/src/cli/lib/aiFiles/guidelinesmd.ts +15 -7
  388. package/src/cli/lib/aiFiles/index.test.ts +188 -222
  389. package/src/cli/lib/aiFiles/index.ts +119 -125
  390. package/src/cli/lib/aiFiles/integration.test.ts +112 -45
  391. package/src/cli/lib/aiFiles/prompt.test.ts +6 -6
  392. package/src/cli/lib/aiFiles/skills.ts +46 -16
  393. package/src/cli/lib/aiFiles/state.test.ts +280 -0
  394. package/src/cli/lib/aiFiles/state.ts +82 -0
  395. package/src/cli/lib/aiFiles/status.ts +45 -39
  396. package/src/cli/lib/aiFiles/utils.test.ts +50 -0
  397. package/src/cli/lib/aiFiles/utils.ts +38 -10
  398. package/src/cli/lib/api.ts +88 -7
  399. package/src/cli/lib/command.ts +12 -7
  400. package/src/cli/lib/config.test.ts +184 -7
  401. package/src/cli/lib/config.ts +67 -7
  402. package/src/cli/lib/deploy2.ts +14 -27
  403. package/src/cli/lib/deployApi/componentDefinition.ts +4 -1
  404. package/src/cli/lib/deploymentSelection.ts +59 -6
  405. package/src/cli/lib/deploymentSelector.test.ts +6 -0
  406. package/src/cli/lib/deploymentSelector.ts +2 -0
  407. package/src/cli/lib/dev.ts +202 -153
  408. package/src/cli/lib/env.ts +0 -15
  409. package/src/cli/lib/envvars.ts +16 -1
  410. package/src/cli/lib/expiration.test.ts +159 -0
  411. package/src/cli/lib/expiration.ts +124 -0
  412. package/src/cli/lib/generatedFunctionLogsApi.ts +16 -1
  413. package/src/cli/lib/init.ts +6 -2
  414. package/src/cli/lib/insights.ts +1 -1
  415. package/src/cli/lib/localDeployment/anonymous.ts +19 -9
  416. package/src/cli/lib/localDeployment/localDeployment.ts +9 -11
  417. package/src/cli/lib/localDeployment/run.ts +1 -1
  418. package/src/cli/lib/localDeployment/upgrade.ts +12 -10
  419. package/src/cli/lib/localDeployment/utils.ts +12 -0
  420. package/src/cli/lib/mcp/tools/status.ts +1 -1
  421. package/src/cli/lib/updates.test.ts +102 -75
  422. package/src/cli/lib/updates.ts +14 -12
  423. package/src/cli/lib/usage.ts +3 -1
  424. package/src/cli/lib/utils/prompts.ts +2 -0
  425. package/src/cli/lib/utils/utils.test.ts +6 -6
  426. package/src/cli/lib/utils/utils.ts +66 -27
  427. package/src/index.ts +1 -1
  428. package/src/react-clerk/ConvexProviderWithClerk.test.tsx +1 -1
  429. package/src/react-clerk/ConvexProviderWithClerk.tsx +1 -1
  430. package/src/server/components/definition.ts +3 -0
  431. package/src/server/components/index.ts +62 -5
  432. package/src/server/data_model.ts +2 -1
  433. package/src/server/impl/meta_impl.ts +74 -0
  434. package/src/server/impl/registration_impl.ts +21 -9
  435. package/src/server/index.ts +8 -0
  436. package/src/server/meta.ts +76 -0
  437. package/src/server/registration.ts +10 -0
  438. package/src/server/schema.test.ts +78 -1
  439. package/dist/cjs/cli/lib/aiFiles/config.js +0 -171
  440. package/dist/cjs/cli/lib/aiFiles/config.js.map +0 -7
  441. package/dist/cjs-types/cli/lib/aiFiles/config.d.ts +0 -46
  442. package/dist/cjs-types/cli/lib/aiFiles/config.d.ts.map +0 -1
  443. package/dist/cjs-types/cli/lib/aiFiles/config.test.d.ts +0 -2
  444. package/dist/cjs-types/cli/lib/aiFiles/config.test.d.ts.map +0 -1
  445. package/dist/esm/cli/lib/aiFiles/config.js +0 -135
  446. package/dist/esm/cli/lib/aiFiles/config.js.map +0 -7
  447. package/dist/esm-types/cli/lib/aiFiles/config.d.ts +0 -46
  448. package/dist/esm-types/cli/lib/aiFiles/config.d.ts.map +0 -1
  449. package/dist/esm-types/cli/lib/aiFiles/config.test.d.ts +0 -2
  450. package/dist/esm-types/cli/lib/aiFiles/config.test.d.ts.map +0 -1
  451. package/src/cli/lib/aiFiles/config.test.ts +0 -460
  452. package/src/cli/lib/aiFiles/config.ts +0 -188
  453. package/src/values/.claude/settings.local.json +0 -10
@@ -56,7 +56,7 @@ vi.mock("../utils/prompts.js", async (importOriginal) => {
56
56
  };
57
57
  });
58
58
 
59
- import { maybeSetupAiFiles } from "./index.js";
59
+ import { attemptSetupAiFiles } from "./index.js";
60
60
 
61
61
  function makeTmpDir(): string {
62
62
  return fs.mkdtempSync(`${os.tmpdir()}${path.sep}`);
@@ -111,7 +111,7 @@ describe("maybeSetupAiFiles interactive prompt", () => {
111
111
  test("user accepts prompt: AI files are installed", async () => {
112
112
  mockPromptYesNo.mockResolvedValue(true);
113
113
 
114
- await maybeSetupAiFiles({ ctx: fakeCtx, convexDir, projectDir: tmpDir });
114
+ await attemptSetupAiFiles({ ctx: fakeCtx, convexDir, projectDir: tmpDir });
115
115
 
116
116
  expect(fs.existsSync(guidelinesPath())).toBe(true);
117
117
  expect(fs.existsSync(statePath())).toBe(true);
@@ -125,7 +125,7 @@ describe("maybeSetupAiFiles interactive prompt", () => {
125
125
  test("user declines prompt: no config and no AI files are written", async () => {
126
126
  mockPromptYesNo.mockResolvedValue(false);
127
127
 
128
- await maybeSetupAiFiles({ ctx: fakeCtx, convexDir, projectDir: tmpDir });
128
+ await attemptSetupAiFiles({ ctx: fakeCtx, convexDir, projectDir: tmpDir });
129
129
 
130
130
  expect(fs.existsSync(guidelinesPath())).toBe(false);
131
131
  expect(fs.existsSync(path.join(tmpDir, "AGENTS.md"))).toBe(false);
@@ -136,7 +136,7 @@ describe("maybeSetupAiFiles interactive prompt", () => {
136
136
  test("non-interactive terminal skips the prompt and does not install AI files", async () => {
137
137
  process.stdin.isTTY = false;
138
138
 
139
- await maybeSetupAiFiles({ ctx: fakeCtx, convexDir, projectDir: tmpDir });
139
+ await attemptSetupAiFiles({ ctx: fakeCtx, convexDir, projectDir: tmpDir });
140
140
 
141
141
  expect(fs.existsSync(guidelinesPath())).toBe(false);
142
142
  expect(fs.existsSync(statePath())).toBe(false);
@@ -163,7 +163,7 @@ describe("maybeSetupAiFiles interactive prompt", () => {
163
163
  ),
164
164
  );
165
165
 
166
- await maybeSetupAiFiles({ ctx: fakeCtx, convexDir, projectDir: tmpDir });
166
+ await attemptSetupAiFiles({ ctx: fakeCtx, convexDir, projectDir: tmpDir });
167
167
 
168
168
  expect(fs.existsSync(path.join(stateDir, "ai-files.state.json"))).toBe(
169
169
  true,
@@ -187,7 +187,7 @@ describe("maybeSetupAiFiles interactive prompt", () => {
187
187
  ].join("\n"),
188
188
  );
189
189
 
190
- await maybeSetupAiFiles({ ctx: fakeCtx, convexDir, projectDir: tmpDir });
190
+ await attemptSetupAiFiles({ ctx: fakeCtx, convexDir, projectDir: tmpDir });
191
191
 
192
192
  expect(fs.existsSync(statePath())).toBe(true);
193
193
  expect(fs.existsSync(guidelinesPath())).toBe(true);
@@ -5,8 +5,10 @@ import { promises as fs } from "fs";
5
5
  import { chalkStderr } from "chalk";
6
6
  import { logMessage } from "../../../bundler/log.js";
7
7
  import { getVersion, fetchAgentSkillsSha } from "../versionApi.js";
8
- import { type AiFilesConfig } from "./config.js";
9
- import { iife, readFileSafe } from "./utils.js";
8
+ import { type AiFilesState } from "./state.js";
9
+ import { exhaustiveCheck, iife, readFileOrNull } from "./utils.js";
10
+
11
+ import { type AiFilesProjectConfig } from "../config.js";
10
12
 
11
13
  /**
12
14
  * Read the frontmatter `name:` values from skills installed by the skills CLI.
@@ -28,7 +30,7 @@ async function readInstalledSkillNames(projectDir: string): Promise<string[]> {
28
30
  const names: string[] = [];
29
31
  for (const entry of entries) {
30
32
  const skillMdPath = path.join(skillsDir, entry, "SKILL.md");
31
- const content = await readFileSafe(skillMdPath);
33
+ const content = await readFileOrNull(skillMdPath);
32
34
  if (content === null) continue;
33
35
  const match = content.match(/^---[\s\S]*?^name:\s*(.+?)\s*$/m);
34
36
  if (match) {
@@ -38,12 +40,28 @@ async function readInstalledSkillNames(projectDir: string): Promise<string[]> {
38
40
  return names;
39
41
  }
40
42
 
43
+ /**
44
+ * Resolve the configured agent list, falling back to defaults.
45
+ */
46
+ function configuredSkillAgents(
47
+ aiFilesConfig?: AiFilesProjectConfig | undefined,
48
+ ): string[] {
49
+ // We default to the two most popular agents for now, "codex" installs to `.agents` which also
50
+ // covers cursor and many other tools. See: https://github.com/vercel-labs/skills?tab=readme-ov-file#supported-agents
51
+ const defaultAgents = ["claude-code", "codex"];
52
+ return aiFilesConfig?.skills?.agents ?? defaultAgents;
53
+ }
54
+
41
55
  /**
42
56
  * Runs `npx skills add get-convex/agent-skills --yes` in the given directory.
43
57
  * Returns true on success, false if the process fails or cannot be started.
44
58
  */
45
- function runSkillsAdd(cwd: string): Promise<boolean> {
46
- return runSkillsCommand(cwd, ["add", "get-convex/agent-skills", "--yes"]);
59
+ function runSkillsAdd(cwd: string, agents: string[]): Promise<boolean> {
60
+ const args = ["add", "get-convex/agent-skills", "--yes"];
61
+ for (const agent of agents) {
62
+ args.push("--agent", agent);
63
+ }
64
+ return runSkillsCommand(cwd, args);
47
65
  }
48
66
 
49
67
  /**
@@ -69,12 +87,15 @@ async function shouldRunSkillsCli(): Promise<boolean> {
69
87
 
70
88
  if (versionData.kind === "error") return true;
71
89
 
72
- if (versionData.data.disableSkillsCli) {
73
- logMessage(chalkStderr.yellow(`Agent skills are temporarily disabled.`));
74
- return false;
90
+ if (versionData.kind === "ok") {
91
+ if (versionData.data.disableSkillsCli) {
92
+ logMessage(chalkStderr.yellow(`Agent skills are temporarily disabled.`));
93
+ return false;
94
+ }
95
+ return true;
75
96
  }
76
97
 
77
- return true;
98
+ return exhaustiveCheck(versionData);
78
99
  }
79
100
 
80
101
  /**
@@ -118,20 +139,24 @@ async function removeSkillsLockIfEmpty({
118
139
  }
119
140
 
120
141
  /**
121
- * Install Convex agent skills and record the SHA and names into the config.
142
+ * Install Convex agent skills and record the SHA and names into the state.
122
143
  * Handles the kill-switch check and all logging internally.
123
144
  */
124
145
  export async function installSkills({
125
146
  projectDir,
126
- config,
147
+ state,
148
+ aiFilesConfig,
127
149
  }: {
128
150
  projectDir: string;
129
- config: AiFilesConfig;
151
+ state: AiFilesState;
152
+ aiFilesConfig?: AiFilesProjectConfig | undefined;
130
153
  }): Promise<void> {
131
154
  if (!(await shouldRunSkillsCli())) return;
155
+ const agents = configuredSkillAgents(aiFilesConfig);
156
+ if (agents.length === 0) return;
132
157
 
133
158
  logMessage("Installing Convex agent skills...");
134
- const skillsOk = await runSkillsAdd(projectDir);
159
+ const skillsOk = await runSkillsAdd(projectDir, agents);
135
160
  if (!skillsOk) {
136
161
  logMessage(
137
162
  chalkStderr.yellow(
@@ -142,10 +167,12 @@ export async function installSkills({
142
167
  }
143
168
 
144
169
  const sha = await fetchAgentSkillsSha();
145
- if (sha) config.agentSkillsSha = sha;
170
+ if (sha) state.agentSkillsSha = sha;
146
171
 
147
172
  const names = await readInstalledSkillNames(projectDir);
148
- if (names.length > 0) config.installedSkillNames = names;
173
+ if (names.length > 0) state.installedSkillNames = names;
174
+
175
+ logMessage(`${chalkStderr.green("✔")} Skills installed`);
149
176
  }
150
177
 
151
178
  /**
@@ -159,7 +186,8 @@ export async function removeInstalledSkills({
159
186
  projectDir: string;
160
187
  skillNames: string[];
161
188
  }): Promise<boolean> {
162
- if (skillNames.length === 0 || !(await shouldRunSkillsCli())) return false;
189
+ if (skillNames.length === 0) return false;
190
+ if (!(await shouldRunSkillsCli())) return false;
163
191
 
164
192
  logMessage(`Removing Convex agent skills: ${skillNames.join(", ")}`);
165
193
  const skillsOk = await runSkillsRemove({ cwd: projectDir, skillNames });
@@ -176,8 +204,10 @@ export async function removeInstalledSkills({
176
204
  projectDir,
177
205
  removedSkillNames: skillNames,
178
206
  });
207
+
179
208
  if (lockRemoved)
180
209
  logMessage(`${chalkStderr.green("✔")} Deleted skills-lock.json.`);
210
+
181
211
  return true;
182
212
  }
183
213
 
@@ -0,0 +1,280 @@
1
+ import { describe, test, expect, vi, beforeEach, afterEach } from "vitest";
2
+ import * as Sentry from "@sentry/node";
3
+ import { promises as fs } from "fs";
4
+ import {
5
+ aiFilesStateSchema,
6
+ attemptReadAiState,
7
+ hasAiState,
8
+ readAiStateOrDefault,
9
+ writeAiState,
10
+ } from "./state.js";
11
+
12
+ vi.mock("@sentry/node", () => ({
13
+ captureException: vi.fn(),
14
+ captureMessage: vi.fn(),
15
+ }));
16
+
17
+ vi.mock("fs", () => ({
18
+ promises: {
19
+ readFile: vi.fn(),
20
+ writeFile: vi.fn(),
21
+ mkdir: vi.fn().mockResolvedValue(undefined),
22
+ },
23
+ }));
24
+
25
+ const mockFs = vi.mocked(fs);
26
+ const mockCaptureException = vi.mocked(Sentry.captureException);
27
+
28
+ const dummyConvexDir = "/tmp/test-project/convex";
29
+
30
+ describe("aiFilesStateSchema", () => {
31
+ test("accepts a fully populated valid state object", () => {
32
+ const result = aiFilesStateSchema.safeParse({
33
+ guidelinesHash: "abc123",
34
+ agentsMdSectionHash: "def456",
35
+ claudeMdHash: "ghi789",
36
+ agentSkillsSha: "deadbeef",
37
+ installedSkillNames: ["convex-migrations", "convex-perf-review"],
38
+ });
39
+ expect(result.success).toBe(true);
40
+ });
41
+
42
+ test("accepts null hashes", () => {
43
+ const result = aiFilesStateSchema.safeParse({
44
+ guidelinesHash: null,
45
+ agentsMdSectionHash: null,
46
+ claudeMdHash: null,
47
+ agentSkillsSha: null,
48
+ });
49
+ expect(result.success).toBe(true);
50
+ });
51
+
52
+ test("applies default for installedSkillNames when absent", () => {
53
+ const result = aiFilesStateSchema.safeParse({
54
+ guidelinesHash: "abc",
55
+ agentsMdSectionHash: null,
56
+ claudeMdHash: null,
57
+ agentSkillsSha: null,
58
+ });
59
+ expect(result.success).toBe(true);
60
+ if (result.success) {
61
+ expect(result.data.installedSkillNames).toEqual([]);
62
+ }
63
+ });
64
+
65
+ test("rejects a number where a string hash is expected", () => {
66
+ const result = aiFilesStateSchema.safeParse({
67
+ guidelinesHash: 123,
68
+ agentsMdSectionHash: null,
69
+ claudeMdHash: null,
70
+ agentSkillsSha: null,
71
+ });
72
+ expect(result.success).toBe(false);
73
+ });
74
+
75
+ test("rejects missing required fields", () => {
76
+ const result = aiFilesStateSchema.safeParse({
77
+ guidelinesHash: "abc",
78
+ });
79
+ expect(result.success).toBe(false);
80
+ });
81
+ });
82
+
83
+ describe("attemptReadAiState", () => {
84
+ beforeEach(() => vi.clearAllMocks());
85
+ afterEach(() => vi.resetAllMocks());
86
+
87
+ test("returns no-file when the state file does not exist", async () => {
88
+ mockFs.readFile.mockRejectedValue(
89
+ Object.assign(new Error("ENOENT"), { code: "ENOENT" }),
90
+ );
91
+
92
+ const result = await attemptReadAiState(dummyConvexDir);
93
+
94
+ expect(result).toEqual({ kind: "no-file" });
95
+ expect(mockCaptureException).not.toHaveBeenCalled();
96
+ });
97
+
98
+ test("returns no-file when the state file is empty", async () => {
99
+ mockFs.readFile.mockResolvedValueOnce("");
100
+
101
+ const result = await attemptReadAiState(dummyConvexDir);
102
+
103
+ expect(result).toEqual({ kind: "no-file" });
104
+ expect(mockCaptureException).not.toHaveBeenCalled();
105
+ });
106
+
107
+ test("throws when reading the state file fails for reasons other than not found", async () => {
108
+ const error = Object.assign(new Error("EACCES"), { code: "EACCES" });
109
+ mockFs.readFile.mockRejectedValueOnce(error);
110
+
111
+ await expect(attemptReadAiState(dummyConvexDir)).rejects.toBe(error);
112
+ expect(mockCaptureException).not.toHaveBeenCalled();
113
+ });
114
+
115
+ test("returns ok with parsed state", async () => {
116
+ mockFs.readFile.mockResolvedValueOnce(
117
+ JSON.stringify({
118
+ guidelinesHash: "abc",
119
+ agentsMdSectionHash: "def",
120
+ claudeMdHash: null,
121
+ agentSkillsSha: null,
122
+ }),
123
+ );
124
+
125
+ const result = await attemptReadAiState(dummyConvexDir);
126
+
127
+ expect(result).toEqual({
128
+ kind: "ok",
129
+ state: {
130
+ guidelinesHash: "abc",
131
+ agentsMdSectionHash: "def",
132
+ claudeMdHash: null,
133
+ agentSkillsSha: null,
134
+ installedSkillNames: [],
135
+ },
136
+ });
137
+ });
138
+
139
+ test("returns parse-error and captures exception when JSON is invalid", async () => {
140
+ mockFs.readFile.mockResolvedValueOnce("not valid json {{{}");
141
+
142
+ const result = await attemptReadAiState(dummyConvexDir);
143
+
144
+ expect(result.kind).toBe("parse-error");
145
+ expect(mockCaptureException).toHaveBeenCalledWith(expect.any(Error));
146
+ });
147
+
148
+ test("returns parse-error and captures exception when schema validation fails", async () => {
149
+ mockFs.readFile.mockResolvedValueOnce(
150
+ JSON.stringify({
151
+ guidelinesHash: 99,
152
+ agentsMdSectionHash: null,
153
+ }),
154
+ );
155
+
156
+ const result = await attemptReadAiState(dummyConvexDir);
157
+
158
+ expect(result.kind).toBe("parse-error");
159
+ expect(mockCaptureException).toHaveBeenCalledWith(expect.any(Error));
160
+ });
161
+ });
162
+
163
+ describe("readAiStateOrDefault", () => {
164
+ beforeEach(() => vi.clearAllMocks());
165
+ afterEach(() => vi.resetAllMocks());
166
+
167
+ test("returns default state when no state file exists", async () => {
168
+ mockFs.readFile.mockRejectedValue(
169
+ Object.assign(new Error("ENOENT"), { code: "ENOENT" }),
170
+ );
171
+
172
+ const result = await readAiStateOrDefault(dummyConvexDir);
173
+
174
+ expect(result).toEqual({
175
+ guidelinesHash: null,
176
+ agentsMdSectionHash: null,
177
+ claudeMdHash: null,
178
+ agentSkillsSha: null,
179
+ installedSkillNames: [],
180
+ });
181
+ });
182
+
183
+ test("returns default state on parse error", async () => {
184
+ mockFs.readFile.mockResolvedValueOnce("not valid json {{{}");
185
+
186
+ const result = await readAiStateOrDefault(dummyConvexDir);
187
+
188
+ expect(result).toEqual({
189
+ guidelinesHash: null,
190
+ agentsMdSectionHash: null,
191
+ claudeMdHash: null,
192
+ agentSkillsSha: null,
193
+ installedSkillNames: [],
194
+ });
195
+ expect(mockCaptureException).toHaveBeenCalled();
196
+ });
197
+
198
+ test("returns parsed state when state file exists", async () => {
199
+ const stored = {
200
+ guidelinesHash: "abc",
201
+ agentsMdSectionHash: "def",
202
+ claudeMdHash: null,
203
+ agentSkillsSha: "deadbeef",
204
+ };
205
+ mockFs.readFile.mockResolvedValueOnce(JSON.stringify(stored));
206
+
207
+ const result = await readAiStateOrDefault(dummyConvexDir);
208
+
209
+ expect(result).toEqual({
210
+ ...stored,
211
+ installedSkillNames: [],
212
+ });
213
+ });
214
+ });
215
+
216
+ describe("hasAiState", () => {
217
+ beforeEach(() => vi.clearAllMocks());
218
+ afterEach(() => vi.resetAllMocks());
219
+
220
+ test("returns false when state file does not exist", async () => {
221
+ mockFs.readFile.mockRejectedValue(
222
+ Object.assign(new Error("ENOENT"), { code: "ENOENT" }),
223
+ );
224
+
225
+ const result = await hasAiState(dummyConvexDir);
226
+
227
+ expect(result).toBe(false);
228
+ expect(mockCaptureException).not.toHaveBeenCalled();
229
+ });
230
+
231
+ test("returns true when a valid state file exists", async () => {
232
+ mockFs.readFile.mockResolvedValueOnce(
233
+ JSON.stringify({
234
+ guidelinesHash: "abc",
235
+ agentsMdSectionHash: "def",
236
+ claudeMdHash: null,
237
+ agentSkillsSha: null,
238
+ }),
239
+ );
240
+
241
+ const result = await hasAiState(dummyConvexDir);
242
+
243
+ expect(result).toBe(true);
244
+ });
245
+
246
+ test("returns false and captures exception when the state file is invalid", async () => {
247
+ mockFs.readFile.mockResolvedValueOnce("not valid json {{{}");
248
+
249
+ const result = await hasAiState(dummyConvexDir);
250
+
251
+ expect(result).toBe(false);
252
+ expect(mockCaptureException).toHaveBeenCalledWith(expect.any(Error));
253
+ });
254
+ });
255
+
256
+ describe("writeAiState", () => {
257
+ beforeEach(() => vi.clearAllMocks());
258
+ afterEach(() => vi.resetAllMocks());
259
+
260
+ test("writes state file", async () => {
261
+ mockFs.writeFile.mockResolvedValue(undefined);
262
+
263
+ const state = {
264
+ guidelinesHash: "abc",
265
+ agentsMdSectionHash: "def",
266
+ claudeMdHash: null,
267
+ agentSkillsSha: null,
268
+ installedSkillNames: ["convex-migrations"],
269
+ };
270
+
271
+ await writeAiState({ state, convexDir: dummyConvexDir });
272
+
273
+ expect(mockFs.writeFile).toHaveBeenCalledTimes(1);
274
+ expect(mockFs.writeFile).toHaveBeenCalledWith(
275
+ expect.stringContaining("ai-files.state.json"),
276
+ JSON.stringify(state, null, 2) + "\n",
277
+ "utf8",
278
+ );
279
+ });
280
+ });
@@ -0,0 +1,82 @@
1
+ import * as Sentry from "@sentry/node";
2
+ // Use raw fs (not ctx.fs) so these operations run asynchronously and don't
3
+ // interfere with the file-watcher used by `convex dev`.
4
+ // eslint-disable-next-line no-restricted-imports
5
+ import { promises as fs } from "fs";
6
+ import { z } from "zod";
7
+ import { aiDirForConvexDir, aiFilesStatePathForConvexDir } from "./paths.js";
8
+ import { attemptReadFile, exhaustiveCheck } from "./utils.js";
9
+
10
+ export const aiFilesStateSchema = z.object({
11
+ guidelinesHash: z.string().nullable(),
12
+ agentsMdSectionHash: z.string().nullable(),
13
+ claudeMdHash: z.string().nullable(),
14
+ // Commit SHA from get-convex/agent-skills that was current when skills were
15
+ // last installed. Used to detect when newer skills are available.
16
+ agentSkillsSha: z.string().nullable(),
17
+ // Names of skills installed by `npx skills add`, used by `remove` to
18
+ // only remove Convex-managed skills.
19
+ installedSkillNames: z.array(z.string()).default([]),
20
+ });
21
+
22
+ export type AiFilesState = z.infer<typeof aiFilesStateSchema>;
23
+
24
+ const DEFAULT_AI_STATE: AiFilesState = {
25
+ guidelinesHash: null,
26
+ agentsMdSectionHash: null,
27
+ claudeMdHash: null,
28
+ agentSkillsSha: null,
29
+ installedSkillNames: [],
30
+ };
31
+
32
+ export type AttemptReadAiStateResult =
33
+ | { kind: "no-file" }
34
+ | { kind: "ok"; state: AiFilesState }
35
+ | { kind: "parse-error"; error: unknown };
36
+
37
+ export async function attemptReadAiState(
38
+ convexDir: string,
39
+ ): Promise<AttemptReadAiStateResult> {
40
+ const result = await attemptReadFile(aiFilesStatePathForConvexDir(convexDir));
41
+ if (result.kind === "not-found" || result.kind === "empty")
42
+ return { kind: "no-file" };
43
+
44
+ try {
45
+ const state = aiFilesStateSchema.parse(JSON.parse(result.content));
46
+ return { kind: "ok", state };
47
+ } catch (error) {
48
+ Sentry.captureException(error);
49
+ return { kind: "parse-error", error };
50
+ }
51
+ }
52
+
53
+ export async function readAiStateOrDefault(
54
+ convexDir: string,
55
+ ): Promise<AiFilesState> {
56
+ const result = await attemptReadAiState(convexDir);
57
+ if (result.kind === "ok") return result.state;
58
+ if (result.kind === "no-file") return { ...DEFAULT_AI_STATE };
59
+ if (result.kind === "parse-error") return { ...DEFAULT_AI_STATE };
60
+ return exhaustiveCheck(result);
61
+ }
62
+
63
+ export async function hasAiState(convexDir: string): Promise<boolean> {
64
+ const result = await attemptReadAiState(convexDir);
65
+ return result.kind === "ok";
66
+ }
67
+
68
+ export async function writeAiState({
69
+ state,
70
+ convexDir,
71
+ }: {
72
+ state: AiFilesState;
73
+ convexDir: string;
74
+ }): Promise<void> {
75
+ const validated = aiFilesStateSchema.parse(state);
76
+ await fs.mkdir(aiDirForConvexDir(convexDir), { recursive: true });
77
+ await fs.writeFile(
78
+ aiFilesStatePathForConvexDir(convexDir),
79
+ JSON.stringify(validated, null, 2) + "\n",
80
+ "utf8",
81
+ );
82
+ }