convex 1.34.0 → 1.35.0

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 (607) hide show
  1. package/CHANGELOG.md +98 -43
  2. package/dist/browser.bundle.js +13 -10
  3. package/dist/browser.bundle.js.map +3 -3
  4. package/dist/cjs/browser/index-node.js +3 -1
  5. package/dist/cjs/browser/index.js +3 -1
  6. package/dist/cjs/browser/index.js.map +2 -2
  7. package/dist/cjs/browser/query_options.js.map +2 -2
  8. package/dist/cjs/browser/sync/authentication_manager.js +4 -1
  9. package/dist/cjs/browser/sync/authentication_manager.js.map +2 -2
  10. package/dist/cjs/browser/sync/web_socket_manager.js +1 -7
  11. package/dist/cjs/browser/sync/web_socket_manager.js.map +2 -2
  12. package/dist/cjs/cli/aiFiles.js +39 -20
  13. package/dist/cjs/cli/aiFiles.js.map +3 -3
  14. package/dist/cjs/cli/codegen_templates/readme.js +14 -1
  15. package/dist/cjs/cli/codegen_templates/readme.js.map +2 -2
  16. package/dist/cjs/cli/configure.js +34 -32
  17. package/dist/cjs/cli/configure.js.map +2 -2
  18. package/dist/cjs/cli/deploy.js +7 -8
  19. package/dist/cjs/cli/deploy.js.map +2 -2
  20. package/dist/cjs/cli/deploymentCreate.js +225 -40
  21. package/dist/cjs/cli/deploymentCreate.js.map +3 -3
  22. package/dist/cjs/cli/deploymentSelect.js +14 -13
  23. package/dist/cjs/cli/deploymentSelect.js.map +2 -2
  24. package/dist/cjs/cli/dev.js +30 -11
  25. package/dist/cjs/cli/dev.js.map +2 -2
  26. package/dist/cjs/cli/docs.js +1 -1
  27. package/dist/cjs/cli/docs.js.map +2 -2
  28. package/dist/cjs/cli/init.js +1 -1
  29. package/dist/cjs/cli/init.js.map +2 -2
  30. package/dist/cjs/cli/lib/aiFiles/agentsmd.js +73 -0
  31. package/dist/cjs/cli/lib/aiFiles/agentsmd.js.map +7 -0
  32. package/dist/cjs/cli/lib/aiFiles/claudemd.js +73 -0
  33. package/dist/cjs/cli/lib/aiFiles/claudemd.js.map +7 -0
  34. package/dist/cjs/cli/lib/aiFiles/cursorrules.js +48 -0
  35. package/dist/cjs/cli/lib/aiFiles/cursorrules.js.map +7 -0
  36. package/dist/cjs/cli/lib/aiFiles/guidelinesmd.js +58 -0
  37. package/dist/cjs/cli/lib/aiFiles/guidelinesmd.js.map +7 -0
  38. package/dist/cjs/cli/lib/aiFiles/index.js +215 -0
  39. package/dist/cjs/cli/lib/aiFiles/index.js.map +7 -0
  40. package/dist/cjs/cli/lib/aiFiles/paths.js.map +7 -0
  41. package/dist/cjs/cli/lib/aiFiles/skills.js +196 -0
  42. package/dist/cjs/cli/lib/aiFiles/skills.js.map +7 -0
  43. package/dist/cjs/cli/lib/aiFiles/state.js +96 -0
  44. package/dist/cjs/cli/lib/aiFiles/state.js.map +7 -0
  45. package/dist/cjs/cli/lib/aiFiles/status.js +198 -0
  46. package/dist/cjs/cli/lib/aiFiles/status.js.map +7 -0
  47. package/dist/cjs/cli/lib/aiFiles/utils.js +128 -0
  48. package/dist/cjs/cli/lib/aiFiles/utils.js.map +7 -0
  49. package/dist/cjs/cli/lib/api.js +70 -7
  50. package/dist/cjs/cli/lib/api.js.map +2 -2
  51. package/dist/cjs/cli/lib/command.js +10 -6
  52. package/dist/cjs/cli/lib/command.js.map +2 -2
  53. package/dist/cjs/cli/lib/config.js +43 -7
  54. package/dist/cjs/cli/lib/config.js.map +3 -3
  55. package/dist/cjs/cli/lib/deploy2.js +9 -26
  56. package/dist/cjs/cli/lib/deploy2.js.map +2 -2
  57. package/dist/cjs/cli/lib/deployApi/componentDefinition.js +4 -1
  58. package/dist/cjs/cli/lib/deployApi/componentDefinition.js.map +2 -2
  59. package/dist/cjs/cli/lib/deploymentSelection.js +45 -2
  60. package/dist/cjs/cli/lib/deploymentSelection.js.map +2 -2
  61. package/dist/cjs/cli/lib/deploymentSelector.js +1 -0
  62. package/dist/cjs/cli/lib/deploymentSelector.js.map +2 -2
  63. package/dist/cjs/cli/lib/dev.js +162 -117
  64. package/dist/cjs/cli/lib/dev.js.map +2 -2
  65. package/dist/cjs/cli/lib/env.js +1 -13
  66. package/dist/cjs/cli/lib/env.js.map +2 -2
  67. package/dist/cjs/cli/lib/expiration.js +104 -0
  68. package/dist/cjs/cli/lib/expiration.js.map +7 -0
  69. package/dist/cjs/cli/lib/generatedFunctionLogsApi.js.map +1 -1
  70. package/dist/cjs/cli/lib/init.js +4 -3
  71. package/dist/cjs/cli/lib/init.js.map +2 -2
  72. package/dist/cjs/cli/lib/insights.js +1 -1
  73. package/dist/cjs/cli/lib/insights.js.map +2 -2
  74. package/dist/cjs/cli/lib/localDeployment/anonymous.js +15 -8
  75. package/dist/cjs/cli/lib/localDeployment/anonymous.js.map +2 -2
  76. package/dist/cjs/cli/lib/localDeployment/localDeployment.js +8 -10
  77. package/dist/cjs/cli/lib/localDeployment/localDeployment.js.map +2 -2
  78. package/dist/cjs/cli/lib/localDeployment/run.js +1 -0
  79. package/dist/cjs/cli/lib/localDeployment/run.js.map +2 -2
  80. package/dist/cjs/cli/lib/localDeployment/upgrade.js +2 -2
  81. package/dist/cjs/cli/lib/localDeployment/upgrade.js.map +2 -2
  82. package/dist/cjs/cli/lib/localDeployment/utils.js +9 -0
  83. package/dist/cjs/cli/lib/localDeployment/utils.js.map +2 -2
  84. package/dist/cjs/cli/lib/mcp/tools/status.js +1 -1
  85. package/dist/cjs/cli/lib/mcp/tools/status.js.map +2 -2
  86. package/dist/cjs/cli/lib/updates.js +12 -13
  87. package/dist/cjs/cli/lib/updates.js.map +2 -2
  88. package/dist/cjs/cli/lib/usage.js +2 -1
  89. package/dist/cjs/cli/lib/usage.js.map +2 -2
  90. package/dist/cjs/cli/lib/utils/prompts.js +2 -1
  91. package/dist/cjs/cli/lib/utils/prompts.js.map +2 -2
  92. package/dist/cjs/cli/lib/utils/utils.js +46 -20
  93. package/dist/cjs/cli/lib/utils/utils.js.map +3 -3
  94. package/dist/cjs/cli/lib/versionApi.js +7 -4
  95. package/dist/cjs/cli/lib/versionApi.js.map +2 -2
  96. package/dist/cjs/cli/lib/workos/workos.js +4 -6
  97. package/dist/cjs/cli/lib/workos/workos.js.map +2 -2
  98. package/dist/cjs/index.js +1 -1
  99. package/dist/cjs/index.js.map +1 -1
  100. package/dist/cjs/react/client.js +43 -6
  101. package/dist/cjs/react/client.js.map +2 -2
  102. package/dist/cjs/react/index.js +2 -0
  103. package/dist/cjs/react/index.js.map +2 -2
  104. package/dist/cjs/react-clerk/ConvexProviderWithClerk.js.map +1 -1
  105. package/dist/cjs/server/api.js.map +2 -2
  106. package/dist/cjs/server/components/definition.js.map +1 -1
  107. package/dist/cjs/server/components/index.js +40 -4
  108. package/dist/cjs/server/components/index.js.map +2 -2
  109. package/dist/cjs/server/data_model.js.map +1 -1
  110. package/dist/cjs/server/impl/meta_impl.js +78 -0
  111. package/dist/cjs/server/impl/meta_impl.js.map +7 -0
  112. package/dist/cjs/server/impl/registration_impl.js +16 -11
  113. package/dist/cjs/server/impl/registration_impl.js.map +2 -2
  114. package/dist/cjs/server/index.js.map +2 -2
  115. package/dist/cjs/server/meta.js +17 -0
  116. package/dist/cjs/server/meta.js.map +7 -0
  117. package/dist/cjs/server/registration.js.map +1 -1
  118. package/dist/cjs-types/browser/index.d.ts +1 -0
  119. package/dist/cjs-types/browser/index.d.ts.map +1 -1
  120. package/dist/cjs-types/browser/query_options.d.ts +12 -9
  121. package/dist/cjs-types/browser/query_options.d.ts.map +1 -1
  122. package/dist/cjs-types/browser/sync/authentication_manager.d.ts.map +1 -1
  123. package/dist/cjs-types/browser/sync/web_socket_manager.d.ts.map +1 -1
  124. package/dist/cjs-types/cli/aiFiles.d.ts.map +1 -1
  125. package/dist/cjs-types/cli/codegen_templates/readme.d.ts.map +1 -1
  126. package/dist/cjs-types/cli/configure.d.ts.map +1 -1
  127. package/dist/cjs-types/cli/configure.test.d.ts +2 -0
  128. package/dist/cjs-types/cli/configure.test.d.ts.map +1 -0
  129. package/dist/cjs-types/cli/deploy.d.ts.map +1 -1
  130. package/dist/cjs-types/cli/deploymentCreate.d.ts +1 -0
  131. package/dist/cjs-types/cli/deploymentCreate.d.ts.map +1 -1
  132. package/dist/cjs-types/cli/deploymentSelect.d.ts +2 -1
  133. package/dist/cjs-types/cli/deploymentSelect.d.ts.map +1 -1
  134. package/dist/cjs-types/cli/dev.d.ts +3 -1
  135. package/dist/cjs-types/cli/dev.d.ts.map +1 -1
  136. package/dist/cjs-types/cli/lib/aiFiles/agentsmd.d.ts +19 -0
  137. package/dist/cjs-types/cli/lib/aiFiles/agentsmd.d.ts.map +1 -0
  138. package/dist/cjs-types/cli/lib/aiFiles/agentsmd.test.d.ts +2 -0
  139. package/dist/cjs-types/cli/lib/aiFiles/agentsmd.test.d.ts.map +1 -0
  140. package/dist/cjs-types/cli/lib/aiFiles/claudemd.d.ts +19 -0
  141. package/dist/cjs-types/cli/lib/aiFiles/claudemd.d.ts.map +1 -0
  142. package/dist/cjs-types/cli/lib/aiFiles/claudemd.test.d.ts +2 -0
  143. package/dist/cjs-types/cli/lib/aiFiles/claudemd.test.d.ts.map +1 -0
  144. package/dist/cjs-types/cli/lib/aiFiles/cursorrules.d.ts +10 -0
  145. package/dist/cjs-types/cli/lib/aiFiles/cursorrules.d.ts.map +1 -0
  146. package/dist/cjs-types/cli/lib/aiFiles/guidelinesmd.d.ts +12 -0
  147. package/dist/cjs-types/cli/lib/aiFiles/guidelinesmd.d.ts.map +1 -0
  148. package/dist/cjs-types/cli/lib/aiFiles/guidelinesmd.test.d.ts +2 -0
  149. package/dist/cjs-types/cli/lib/aiFiles/guidelinesmd.test.d.ts.map +1 -0
  150. package/dist/cjs-types/cli/lib/aiFiles/index.d.ts +42 -0
  151. package/dist/cjs-types/cli/lib/aiFiles/index.d.ts.map +1 -0
  152. package/dist/cjs-types/cli/lib/aiFiles/index.test.d.ts.map +1 -0
  153. package/dist/cjs-types/cli/lib/aiFiles/integration.test.d.ts.map +1 -0
  154. package/dist/cjs-types/cli/lib/{ai → aiFiles}/paths.d.ts +4 -0
  155. package/dist/cjs-types/cli/lib/aiFiles/paths.d.ts.map +1 -0
  156. package/dist/cjs-types/cli/lib/aiFiles/prompt.test.d.ts.map +1 -0
  157. package/dist/cjs-types/cli/lib/aiFiles/skills.d.ts +20 -0
  158. package/dist/cjs-types/cli/lib/aiFiles/skills.d.ts.map +1 -0
  159. package/dist/cjs-types/cli/lib/aiFiles/state.d.ts +38 -0
  160. package/dist/cjs-types/cli/lib/aiFiles/state.d.ts.map +1 -0
  161. package/dist/cjs-types/cli/lib/aiFiles/state.test.d.ts +2 -0
  162. package/dist/cjs-types/cli/lib/aiFiles/state.test.d.ts.map +1 -0
  163. package/dist/cjs-types/cli/lib/aiFiles/status.d.ts +6 -0
  164. package/dist/cjs-types/cli/lib/aiFiles/status.d.ts.map +1 -0
  165. package/dist/cjs-types/cli/lib/aiFiles/utils.d.ts +56 -0
  166. package/dist/cjs-types/cli/lib/aiFiles/utils.d.ts.map +1 -0
  167. package/dist/cjs-types/cli/lib/aiFiles/utils.test.d.ts +2 -0
  168. package/dist/cjs-types/cli/lib/aiFiles/utils.test.d.ts.map +1 -0
  169. package/dist/cjs-types/cli/lib/api.d.ts +3 -3
  170. package/dist/cjs-types/cli/lib/api.d.ts.map +1 -1
  171. package/dist/cjs-types/cli/lib/command.d.ts +2 -1
  172. package/dist/cjs-types/cli/lib/command.d.ts.map +1 -1
  173. package/dist/cjs-types/cli/lib/config.d.ts +18 -6
  174. package/dist/cjs-types/cli/lib/config.d.ts.map +1 -1
  175. package/dist/cjs-types/cli/lib/deploy2.d.ts +5 -2
  176. package/dist/cjs-types/cli/lib/deploy2.d.ts.map +1 -1
  177. package/dist/cjs-types/cli/lib/deployApi/componentDefinition.d.ts +27 -12
  178. package/dist/cjs-types/cli/lib/deployApi/componentDefinition.d.ts.map +1 -1
  179. package/dist/cjs-types/cli/lib/deployApi/definitionConfig.d.ts +24 -24
  180. package/dist/cjs-types/cli/lib/deployApi/modules.d.ts +14 -14
  181. package/dist/cjs-types/cli/lib/deployApi/startPush.d.ts +61 -52
  182. package/dist/cjs-types/cli/lib/deployApi/startPush.d.ts.map +1 -1
  183. package/dist/cjs-types/cli/lib/deploymentSelection.d.ts.map +1 -1
  184. package/dist/cjs-types/cli/lib/deploymentSelector.d.ts +2 -0
  185. package/dist/cjs-types/cli/lib/deploymentSelector.d.ts.map +1 -1
  186. package/dist/cjs-types/cli/lib/dev.d.ts.map +1 -1
  187. package/dist/cjs-types/cli/lib/env.d.ts +0 -4
  188. package/dist/cjs-types/cli/lib/env.d.ts.map +1 -1
  189. package/dist/cjs-types/cli/lib/expiration.d.ts +35 -0
  190. package/dist/cjs-types/cli/lib/expiration.d.ts.map +1 -0
  191. package/dist/cjs-types/cli/lib/expiration.test.d.ts +2 -0
  192. package/dist/cjs-types/cli/lib/expiration.test.d.ts.map +1 -0
  193. package/dist/cjs-types/cli/lib/generatedFunctionLogsApi.d.ts +16 -1
  194. package/dist/cjs-types/cli/lib/generatedFunctionLogsApi.d.ts.map +1 -1
  195. package/dist/cjs-types/cli/lib/init.d.ts.map +1 -1
  196. package/dist/cjs-types/cli/lib/localDeployment/anonymous.d.ts.map +1 -1
  197. package/dist/cjs-types/cli/lib/localDeployment/localDeployment.d.ts.map +1 -1
  198. package/dist/cjs-types/cli/lib/localDeployment/run.d.ts +15 -0
  199. package/dist/cjs-types/cli/lib/localDeployment/run.d.ts.map +1 -1
  200. package/dist/cjs-types/cli/lib/localDeployment/upgrade.d.ts.map +1 -1
  201. package/dist/cjs-types/cli/lib/localDeployment/utils.d.ts +7 -0
  202. package/dist/cjs-types/cli/lib/localDeployment/utils.d.ts.map +1 -1
  203. package/dist/cjs-types/cli/lib/mcp/requestContext.d.ts +3 -3
  204. package/dist/cjs-types/cli/lib/mcp/tools/insights.d.ts +2 -2
  205. package/dist/cjs-types/cli/lib/updates.d.ts +4 -3
  206. package/dist/cjs-types/cli/lib/updates.d.ts.map +1 -1
  207. package/dist/cjs-types/cli/lib/usage.d.ts.map +1 -1
  208. package/dist/cjs-types/cli/lib/utils/prompts.d.ts +1 -0
  209. package/dist/cjs-types/cli/lib/utils/prompts.d.ts.map +1 -1
  210. package/dist/cjs-types/cli/lib/utils/utils.d.ts +16 -2
  211. package/dist/cjs-types/cli/lib/utils/utils.d.ts.map +1 -1
  212. package/dist/cjs-types/cli/lib/versionApi.d.ts +7 -1
  213. package/dist/cjs-types/cli/lib/versionApi.d.ts.map +1 -1
  214. package/dist/cjs-types/cli/lib/workos/workos.d.ts.map +1 -1
  215. package/dist/cjs-types/index.d.ts +1 -1
  216. package/dist/cjs-types/react/client.d.ts +54 -2
  217. package/dist/cjs-types/react/client.d.ts.map +1 -1
  218. package/dist/cjs-types/react/index.d.ts +7 -2
  219. package/dist/cjs-types/react/index.d.ts.map +1 -1
  220. package/dist/cjs-types/react/use_query_object_options.test.d.ts +5 -0
  221. package/dist/cjs-types/react/use_query_object_options.test.d.ts.map +1 -0
  222. package/dist/cjs-types/react/use_query_result.test.d.ts +5 -0
  223. package/dist/cjs-types/react/use_query_result.test.d.ts.map +1 -0
  224. package/dist/cjs-types/react-clerk/ConvexProviderWithClerk.d.ts +1 -1
  225. package/dist/cjs-types/server/api.d.ts +5 -1
  226. package/dist/cjs-types/server/api.d.ts.map +1 -1
  227. package/dist/cjs-types/server/components/definition.d.ts +1 -0
  228. package/dist/cjs-types/server/components/definition.d.ts.map +1 -1
  229. package/dist/cjs-types/server/components/index.d.ts +5 -1
  230. package/dist/cjs-types/server/components/index.d.ts.map +1 -1
  231. package/dist/cjs-types/server/data_model.d.ts +2 -1
  232. package/dist/cjs-types/server/data_model.d.ts.map +1 -1
  233. package/dist/cjs-types/server/impl/meta_impl.d.ts +5 -0
  234. package/dist/cjs-types/server/impl/meta_impl.d.ts.map +1 -0
  235. package/dist/cjs-types/server/impl/registration_impl.d.ts.map +1 -1
  236. package/dist/cjs-types/server/index.d.ts +1 -0
  237. package/dist/cjs-types/server/index.d.ts.map +1 -1
  238. package/dist/cjs-types/server/meta.d.ts +72 -0
  239. package/dist/cjs-types/server/meta.d.ts.map +1 -0
  240. package/dist/cjs-types/server/registration.d.ts.map +1 -1
  241. package/dist/cli.bundle.cjs +2446 -1933
  242. package/dist/cli.bundle.cjs.map +4 -4
  243. package/dist/esm/browser/index-node.js +1 -0
  244. package/dist/esm/browser/index.js +1 -0
  245. package/dist/esm/browser/index.js.map +2 -2
  246. package/dist/esm/browser/query_options.js.map +2 -2
  247. package/dist/esm/browser/sync/authentication_manager.js +4 -1
  248. package/dist/esm/browser/sync/authentication_manager.js.map +2 -2
  249. package/dist/esm/browser/sync/web_socket_manager.js +1 -7
  250. package/dist/esm/browser/sync/web_socket_manager.js.map +2 -2
  251. package/dist/esm/cli/aiFiles.js +41 -23
  252. package/dist/esm/cli/aiFiles.js.map +2 -2
  253. package/dist/esm/cli/codegen_templates/readme.js +14 -1
  254. package/dist/esm/cli/codegen_templates/readme.js.map +2 -2
  255. package/dist/esm/cli/configure.js +35 -33
  256. package/dist/esm/cli/configure.js.map +2 -2
  257. package/dist/esm/cli/deploy.js +11 -10
  258. package/dist/esm/cli/deploy.js.map +2 -2
  259. package/dist/esm/cli/deploymentCreate.js +238 -42
  260. package/dist/esm/cli/deploymentCreate.js.map +2 -2
  261. package/dist/esm/cli/deploymentSelect.js +13 -12
  262. package/dist/esm/cli/deploymentSelect.js.map +2 -2
  263. package/dist/esm/cli/dev.js +34 -13
  264. package/dist/esm/cli/dev.js.map +2 -2
  265. package/dist/esm/cli/docs.js +1 -1
  266. package/dist/esm/cli/docs.js.map +2 -2
  267. package/dist/esm/cli/init.js +2 -2
  268. package/dist/esm/cli/init.js.map +2 -2
  269. package/dist/esm/cli/lib/aiFiles/agentsmd.js +56 -0
  270. package/dist/esm/cli/lib/aiFiles/agentsmd.js.map +7 -0
  271. package/dist/esm/cli/lib/aiFiles/claudemd.js +56 -0
  272. package/dist/esm/cli/lib/aiFiles/claudemd.js.map +7 -0
  273. package/dist/esm/cli/lib/aiFiles/cursorrules.js +16 -0
  274. package/dist/esm/cli/lib/aiFiles/cursorrules.js.map +7 -0
  275. package/dist/esm/cli/lib/aiFiles/guidelinesmd.js +35 -0
  276. package/dist/esm/cli/lib/aiFiles/guidelinesmd.js.map +7 -0
  277. package/dist/esm/cli/lib/aiFiles/index.js +193 -0
  278. package/dist/esm/cli/lib/aiFiles/index.js.map +7 -0
  279. package/dist/esm/cli/lib/aiFiles/paths.js.map +7 -0
  280. package/dist/esm/cli/lib/aiFiles/skills.js +163 -0
  281. package/dist/esm/cli/lib/aiFiles/skills.js.map +7 -0
  282. package/dist/esm/cli/lib/aiFiles/state.js +60 -0
  283. package/dist/esm/cli/lib/aiFiles/state.js.map +7 -0
  284. package/dist/esm/cli/lib/aiFiles/status.js +178 -0
  285. package/dist/esm/cli/lib/aiFiles/status.js.map +7 -0
  286. package/dist/esm/cli/lib/aiFiles/utils.js +97 -0
  287. package/dist/esm/cli/lib/aiFiles/utils.js.map +7 -0
  288. package/dist/esm/cli/lib/api.js +70 -7
  289. package/dist/esm/cli/lib/api.js.map +2 -2
  290. package/dist/esm/cli/lib/command.js +10 -6
  291. package/dist/esm/cli/lib/command.js.map +2 -2
  292. package/dist/esm/cli/lib/config.js +41 -6
  293. package/dist/esm/cli/lib/config.js.map +2 -2
  294. package/dist/esm/cli/lib/deploy2.js +13 -26
  295. package/dist/esm/cli/lib/deploy2.js.map +2 -2
  296. package/dist/esm/cli/lib/deployApi/componentDefinition.js +4 -1
  297. package/dist/esm/cli/lib/deployApi/componentDefinition.js.map +2 -2
  298. package/dist/esm/cli/lib/deploymentSelection.js +46 -2
  299. package/dist/esm/cli/lib/deploymentSelection.js.map +2 -2
  300. package/dist/esm/cli/lib/deploymentSelector.js +1 -0
  301. package/dist/esm/cli/lib/deploymentSelector.js.map +2 -2
  302. package/dist/esm/cli/lib/dev.js +162 -118
  303. package/dist/esm/cli/lib/dev.js.map +2 -2
  304. package/dist/esm/cli/lib/env.js +0 -11
  305. package/dist/esm/cli/lib/env.js.map +2 -2
  306. package/dist/esm/cli/lib/expiration.js +80 -0
  307. package/dist/esm/cli/lib/expiration.js.map +7 -0
  308. package/dist/esm/cli/lib/init.js +4 -3
  309. package/dist/esm/cli/lib/init.js.map +2 -2
  310. package/dist/esm/cli/lib/insights.js +1 -1
  311. package/dist/esm/cli/lib/insights.js.map +2 -2
  312. package/dist/esm/cli/lib/localDeployment/anonymous.js +16 -9
  313. package/dist/esm/cli/lib/localDeployment/anonymous.js.map +2 -2
  314. package/dist/esm/cli/lib/localDeployment/localDeployment.js +9 -11
  315. package/dist/esm/cli/lib/localDeployment/localDeployment.js.map +2 -2
  316. package/dist/esm/cli/lib/localDeployment/run.js +1 -1
  317. package/dist/esm/cli/lib/localDeployment/run.js.map +2 -2
  318. package/dist/esm/cli/lib/localDeployment/upgrade.js +2 -2
  319. package/dist/esm/cli/lib/localDeployment/upgrade.js.map +2 -2
  320. package/dist/esm/cli/lib/localDeployment/utils.js +8 -0
  321. package/dist/esm/cli/lib/localDeployment/utils.js.map +2 -2
  322. package/dist/esm/cli/lib/mcp/tools/status.js +1 -1
  323. package/dist/esm/cli/lib/mcp/tools/status.js.map +2 -2
  324. package/dist/esm/cli/lib/updates.js +14 -12
  325. package/dist/esm/cli/lib/updates.js.map +2 -2
  326. package/dist/esm/cli/lib/usage.js +2 -1
  327. package/dist/esm/cli/lib/usage.js.map +2 -2
  328. package/dist/esm/cli/lib/utils/prompts.js +2 -1
  329. package/dist/esm/cli/lib/utils/prompts.js.map +2 -2
  330. package/dist/esm/cli/lib/utils/utils.js +45 -20
  331. package/dist/esm/cli/lib/utils/utils.js.map +3 -3
  332. package/dist/esm/cli/lib/versionApi.js +7 -4
  333. package/dist/esm/cli/lib/versionApi.js.map +2 -2
  334. package/dist/esm/cli/lib/workos/workos.js +4 -6
  335. package/dist/esm/cli/lib/workos/workos.js.map +2 -2
  336. package/dist/esm/index.js +1 -1
  337. package/dist/esm/index.js.map +1 -1
  338. package/dist/esm/react/client.js +43 -6
  339. package/dist/esm/react/client.js.map +2 -2
  340. package/dist/esm/react/index.js +1 -0
  341. package/dist/esm/react/index.js.map +2 -2
  342. package/dist/esm/react-clerk/ConvexProviderWithClerk.js.map +1 -1
  343. package/dist/esm/server/api.js.map +2 -2
  344. package/dist/esm/server/components/index.js +40 -4
  345. package/dist/esm/server/components/index.js.map +2 -2
  346. package/dist/esm/server/impl/meta_impl.js +54 -0
  347. package/dist/esm/server/impl/meta_impl.js.map +7 -0
  348. package/dist/esm/server/impl/registration_impl.js +20 -11
  349. package/dist/esm/server/impl/registration_impl.js.map +2 -2
  350. package/dist/esm/server/index.js.map +2 -2
  351. package/dist/esm/server/meta.js +2 -0
  352. package/dist/esm/server/meta.js.map +7 -0
  353. package/dist/esm-types/browser/index.d.ts +1 -0
  354. package/dist/esm-types/browser/index.d.ts.map +1 -1
  355. package/dist/esm-types/browser/query_options.d.ts +12 -9
  356. package/dist/esm-types/browser/query_options.d.ts.map +1 -1
  357. package/dist/esm-types/browser/sync/authentication_manager.d.ts.map +1 -1
  358. package/dist/esm-types/browser/sync/web_socket_manager.d.ts.map +1 -1
  359. package/dist/esm-types/cli/aiFiles.d.ts.map +1 -1
  360. package/dist/esm-types/cli/codegen_templates/readme.d.ts.map +1 -1
  361. package/dist/esm-types/cli/configure.d.ts.map +1 -1
  362. package/dist/esm-types/cli/configure.test.d.ts +2 -0
  363. package/dist/esm-types/cli/configure.test.d.ts.map +1 -0
  364. package/dist/esm-types/cli/deploy.d.ts.map +1 -1
  365. package/dist/esm-types/cli/deploymentCreate.d.ts +1 -0
  366. package/dist/esm-types/cli/deploymentCreate.d.ts.map +1 -1
  367. package/dist/esm-types/cli/deploymentSelect.d.ts +2 -1
  368. package/dist/esm-types/cli/deploymentSelect.d.ts.map +1 -1
  369. package/dist/esm-types/cli/dev.d.ts +3 -1
  370. package/dist/esm-types/cli/dev.d.ts.map +1 -1
  371. package/dist/esm-types/cli/lib/aiFiles/agentsmd.d.ts +19 -0
  372. package/dist/esm-types/cli/lib/aiFiles/agentsmd.d.ts.map +1 -0
  373. package/dist/esm-types/cli/lib/aiFiles/agentsmd.test.d.ts +2 -0
  374. package/dist/esm-types/cli/lib/aiFiles/agentsmd.test.d.ts.map +1 -0
  375. package/dist/esm-types/cli/lib/aiFiles/claudemd.d.ts +19 -0
  376. package/dist/esm-types/cli/lib/aiFiles/claudemd.d.ts.map +1 -0
  377. package/dist/esm-types/cli/lib/aiFiles/claudemd.test.d.ts +2 -0
  378. package/dist/esm-types/cli/lib/aiFiles/claudemd.test.d.ts.map +1 -0
  379. package/dist/esm-types/cli/lib/aiFiles/cursorrules.d.ts +10 -0
  380. package/dist/esm-types/cli/lib/aiFiles/cursorrules.d.ts.map +1 -0
  381. package/dist/esm-types/cli/lib/aiFiles/guidelinesmd.d.ts +12 -0
  382. package/dist/esm-types/cli/lib/aiFiles/guidelinesmd.d.ts.map +1 -0
  383. package/dist/esm-types/cli/lib/aiFiles/guidelinesmd.test.d.ts +2 -0
  384. package/dist/esm-types/cli/lib/aiFiles/guidelinesmd.test.d.ts.map +1 -0
  385. package/dist/esm-types/cli/lib/aiFiles/index.d.ts +42 -0
  386. package/dist/esm-types/cli/lib/aiFiles/index.d.ts.map +1 -0
  387. package/dist/esm-types/cli/lib/aiFiles/index.test.d.ts.map +1 -0
  388. package/dist/esm-types/cli/lib/aiFiles/integration.test.d.ts.map +1 -0
  389. package/dist/esm-types/cli/lib/{ai → aiFiles}/paths.d.ts +4 -0
  390. package/dist/esm-types/cli/lib/aiFiles/paths.d.ts.map +1 -0
  391. package/dist/esm-types/cli/lib/aiFiles/prompt.test.d.ts.map +1 -0
  392. package/dist/esm-types/cli/lib/aiFiles/skills.d.ts +20 -0
  393. package/dist/esm-types/cli/lib/aiFiles/skills.d.ts.map +1 -0
  394. package/dist/esm-types/cli/lib/aiFiles/state.d.ts +38 -0
  395. package/dist/esm-types/cli/lib/aiFiles/state.d.ts.map +1 -0
  396. package/dist/esm-types/cli/lib/aiFiles/state.test.d.ts +2 -0
  397. package/dist/esm-types/cli/lib/aiFiles/state.test.d.ts.map +1 -0
  398. package/dist/esm-types/cli/lib/aiFiles/status.d.ts +6 -0
  399. package/dist/esm-types/cli/lib/aiFiles/status.d.ts.map +1 -0
  400. package/dist/esm-types/cli/lib/aiFiles/utils.d.ts +56 -0
  401. package/dist/esm-types/cli/lib/aiFiles/utils.d.ts.map +1 -0
  402. package/dist/esm-types/cli/lib/aiFiles/utils.test.d.ts +2 -0
  403. package/dist/esm-types/cli/lib/aiFiles/utils.test.d.ts.map +1 -0
  404. package/dist/esm-types/cli/lib/api.d.ts +3 -3
  405. package/dist/esm-types/cli/lib/api.d.ts.map +1 -1
  406. package/dist/esm-types/cli/lib/command.d.ts +2 -1
  407. package/dist/esm-types/cli/lib/command.d.ts.map +1 -1
  408. package/dist/esm-types/cli/lib/config.d.ts +18 -6
  409. package/dist/esm-types/cli/lib/config.d.ts.map +1 -1
  410. package/dist/esm-types/cli/lib/deploy2.d.ts +5 -2
  411. package/dist/esm-types/cli/lib/deploy2.d.ts.map +1 -1
  412. package/dist/esm-types/cli/lib/deployApi/componentDefinition.d.ts +27 -12
  413. package/dist/esm-types/cli/lib/deployApi/componentDefinition.d.ts.map +1 -1
  414. package/dist/esm-types/cli/lib/deployApi/definitionConfig.d.ts +24 -24
  415. package/dist/esm-types/cli/lib/deployApi/modules.d.ts +14 -14
  416. package/dist/esm-types/cli/lib/deployApi/startPush.d.ts +61 -52
  417. package/dist/esm-types/cli/lib/deployApi/startPush.d.ts.map +1 -1
  418. package/dist/esm-types/cli/lib/deploymentSelection.d.ts.map +1 -1
  419. package/dist/esm-types/cli/lib/deploymentSelector.d.ts +2 -0
  420. package/dist/esm-types/cli/lib/deploymentSelector.d.ts.map +1 -1
  421. package/dist/esm-types/cli/lib/dev.d.ts.map +1 -1
  422. package/dist/esm-types/cli/lib/env.d.ts +0 -4
  423. package/dist/esm-types/cli/lib/env.d.ts.map +1 -1
  424. package/dist/esm-types/cli/lib/expiration.d.ts +35 -0
  425. package/dist/esm-types/cli/lib/expiration.d.ts.map +1 -0
  426. package/dist/esm-types/cli/lib/expiration.test.d.ts +2 -0
  427. package/dist/esm-types/cli/lib/expiration.test.d.ts.map +1 -0
  428. package/dist/esm-types/cli/lib/generatedFunctionLogsApi.d.ts +16 -1
  429. package/dist/esm-types/cli/lib/generatedFunctionLogsApi.d.ts.map +1 -1
  430. package/dist/esm-types/cli/lib/init.d.ts.map +1 -1
  431. package/dist/esm-types/cli/lib/localDeployment/anonymous.d.ts.map +1 -1
  432. package/dist/esm-types/cli/lib/localDeployment/localDeployment.d.ts.map +1 -1
  433. package/dist/esm-types/cli/lib/localDeployment/run.d.ts +15 -0
  434. package/dist/esm-types/cli/lib/localDeployment/run.d.ts.map +1 -1
  435. package/dist/esm-types/cli/lib/localDeployment/upgrade.d.ts.map +1 -1
  436. package/dist/esm-types/cli/lib/localDeployment/utils.d.ts +7 -0
  437. package/dist/esm-types/cli/lib/localDeployment/utils.d.ts.map +1 -1
  438. package/dist/esm-types/cli/lib/mcp/requestContext.d.ts +3 -3
  439. package/dist/esm-types/cli/lib/mcp/tools/insights.d.ts +2 -2
  440. package/dist/esm-types/cli/lib/updates.d.ts +4 -3
  441. package/dist/esm-types/cli/lib/updates.d.ts.map +1 -1
  442. package/dist/esm-types/cli/lib/usage.d.ts.map +1 -1
  443. package/dist/esm-types/cli/lib/utils/prompts.d.ts +1 -0
  444. package/dist/esm-types/cli/lib/utils/prompts.d.ts.map +1 -1
  445. package/dist/esm-types/cli/lib/utils/utils.d.ts +16 -2
  446. package/dist/esm-types/cli/lib/utils/utils.d.ts.map +1 -1
  447. package/dist/esm-types/cli/lib/versionApi.d.ts +7 -1
  448. package/dist/esm-types/cli/lib/versionApi.d.ts.map +1 -1
  449. package/dist/esm-types/cli/lib/workos/workos.d.ts.map +1 -1
  450. package/dist/esm-types/index.d.ts +1 -1
  451. package/dist/esm-types/react/client.d.ts +54 -2
  452. package/dist/esm-types/react/client.d.ts.map +1 -1
  453. package/dist/esm-types/react/index.d.ts +7 -2
  454. package/dist/esm-types/react/index.d.ts.map +1 -1
  455. package/dist/esm-types/react/use_query_object_options.test.d.ts +5 -0
  456. package/dist/esm-types/react/use_query_object_options.test.d.ts.map +1 -0
  457. package/dist/esm-types/react/use_query_result.test.d.ts +5 -0
  458. package/dist/esm-types/react/use_query_result.test.d.ts.map +1 -0
  459. package/dist/esm-types/react-clerk/ConvexProviderWithClerk.d.ts +1 -1
  460. package/dist/esm-types/server/api.d.ts +5 -1
  461. package/dist/esm-types/server/api.d.ts.map +1 -1
  462. package/dist/esm-types/server/components/definition.d.ts +1 -0
  463. package/dist/esm-types/server/components/definition.d.ts.map +1 -1
  464. package/dist/esm-types/server/components/index.d.ts +5 -1
  465. package/dist/esm-types/server/components/index.d.ts.map +1 -1
  466. package/dist/esm-types/server/data_model.d.ts +2 -1
  467. package/dist/esm-types/server/data_model.d.ts.map +1 -1
  468. package/dist/esm-types/server/impl/meta_impl.d.ts +5 -0
  469. package/dist/esm-types/server/impl/meta_impl.d.ts.map +1 -0
  470. package/dist/esm-types/server/impl/registration_impl.d.ts.map +1 -1
  471. package/dist/esm-types/server/index.d.ts +1 -0
  472. package/dist/esm-types/server/index.d.ts.map +1 -1
  473. package/dist/esm-types/server/meta.d.ts +72 -0
  474. package/dist/esm-types/server/meta.d.ts.map +1 -0
  475. package/dist/esm-types/server/registration.d.ts.map +1 -1
  476. package/dist/react.bundle.js +55 -15
  477. package/dist/react.bundle.js.map +3 -3
  478. package/package.json +11 -7
  479. package/schemas/convex.schema.json +22 -3
  480. package/src/browser/index.ts +3 -0
  481. package/src/browser/query_options.test.ts +0 -9
  482. package/src/browser/query_options.ts +36 -15
  483. package/src/browser/sync/authentication_manager.ts +9 -4
  484. package/src/browser/sync/client_node.test.ts +125 -0
  485. package/src/browser/sync/web_socket_manager.ts +1 -7
  486. package/src/cli/aiFiles.ts +56 -33
  487. package/src/cli/codegen_templates/readme.ts +14 -1
  488. package/src/cli/configure.test.ts +138 -0
  489. package/src/cli/configure.ts +62 -55
  490. package/src/cli/deploy.ts +12 -9
  491. package/src/cli/deploymentCreate.test.ts +349 -14
  492. package/src/cli/deploymentCreate.ts +268 -41
  493. package/src/cli/deploymentSelect.test.ts +136 -27
  494. package/src/cli/deploymentSelect.ts +50 -41
  495. package/src/cli/deploymentSelection.test.ts +399 -37
  496. package/src/cli/dev.ts +49 -14
  497. package/src/cli/docs.ts +1 -1
  498. package/src/cli/init.ts +2 -2
  499. package/src/cli/lib/{ai → aiFiles}/MANUAL_TESTING.md +6 -2
  500. package/src/cli/lib/aiFiles/agentsmd.test.ts +133 -0
  501. package/src/cli/lib/aiFiles/agentsmd.ts +81 -0
  502. package/src/cli/lib/aiFiles/claudemd.test.ts +92 -0
  503. package/src/cli/lib/aiFiles/claudemd.ts +81 -0
  504. package/src/cli/lib/aiFiles/cursorrules.ts +25 -0
  505. package/src/cli/lib/aiFiles/guidelinesmd.test.ts +50 -0
  506. package/src/cli/lib/aiFiles/guidelinesmd.ts +49 -0
  507. package/src/cli/lib/{ai → aiFiles}/index.test.ts +343 -516
  508. package/src/cli/lib/aiFiles/index.ts +297 -0
  509. package/src/cli/lib/{ai → aiFiles}/integration.test.ts +195 -158
  510. package/src/cli/lib/{ai → aiFiles}/paths.ts +5 -0
  511. package/src/cli/lib/{ai → aiFiles}/prompt.test.ts +79 -31
  512. package/src/cli/lib/aiFiles/skills.ts +243 -0
  513. package/src/cli/lib/aiFiles/state.test.ts +280 -0
  514. package/src/cli/lib/aiFiles/state.ts +82 -0
  515. package/src/cli/lib/aiFiles/status.ts +246 -0
  516. package/src/cli/lib/aiFiles/utils.test.ts +50 -0
  517. package/src/cli/lib/aiFiles/utils.ts +191 -0
  518. package/src/cli/lib/api.ts +88 -7
  519. package/src/cli/lib/command.ts +18 -8
  520. package/src/cli/lib/config.test.ts +185 -8
  521. package/src/cli/lib/config.ts +73 -12
  522. package/src/cli/lib/deploy2.ts +14 -27
  523. package/src/cli/lib/deployApi/componentDefinition.ts +4 -1
  524. package/src/cli/lib/deploymentSelection.ts +59 -6
  525. package/src/cli/lib/deploymentSelector.test.ts +6 -0
  526. package/src/cli/lib/deploymentSelector.ts +2 -0
  527. package/src/cli/lib/dev.ts +202 -153
  528. package/src/cli/lib/env.ts +0 -15
  529. package/src/cli/lib/expiration.test.ts +159 -0
  530. package/src/cli/lib/expiration.ts +124 -0
  531. package/src/cli/lib/generatedFunctionLogsApi.ts +16 -1
  532. package/src/cli/lib/init.ts +6 -2
  533. package/src/cli/lib/insights.ts +1 -1
  534. package/src/cli/lib/localDeployment/anonymous.ts +19 -9
  535. package/src/cli/lib/localDeployment/localDeployment.ts +9 -11
  536. package/src/cli/lib/localDeployment/run.ts +1 -1
  537. package/src/cli/lib/localDeployment/upgrade.ts +12 -10
  538. package/src/cli/lib/localDeployment/utils.ts +12 -0
  539. package/src/cli/lib/mcp/tools/status.ts +1 -1
  540. package/src/cli/lib/updates.test.ts +97 -60
  541. package/src/cli/lib/updates.ts +17 -15
  542. package/src/cli/lib/usage.ts +3 -1
  543. package/src/cli/lib/utils/prompts.ts +2 -0
  544. package/src/cli/lib/utils/utils.test.ts +6 -6
  545. package/src/cli/lib/utils/utils.ts +66 -27
  546. package/src/cli/lib/versionApi.test.ts +13 -10
  547. package/src/cli/lib/versionApi.ts +13 -5
  548. package/src/cli/lib/workos/workos.ts +4 -5
  549. package/src/index.ts +1 -1
  550. package/src/react/client.test.tsx +65 -0
  551. package/src/react/client.ts +129 -13
  552. package/src/react/index.ts +9 -1
  553. package/src/react/use_query_object_options.test.ts +50 -0
  554. package/src/react/use_query_result.test.ts +41 -0
  555. package/src/react-clerk/ConvexProviderWithClerk.test.tsx +1 -1
  556. package/src/react-clerk/ConvexProviderWithClerk.tsx +1 -1
  557. package/src/server/api.ts +5 -1
  558. package/src/server/components/definition.ts +3 -0
  559. package/src/server/components/index.ts +62 -5
  560. package/src/server/data_model.ts +2 -1
  561. package/src/server/impl/meta_impl.ts +74 -0
  562. package/src/server/impl/registration_impl.ts +21 -9
  563. package/src/server/index.ts +8 -0
  564. package/src/server/meta.ts +76 -0
  565. package/src/server/registration.ts +10 -0
  566. package/src/server/schema.test.ts +78 -1
  567. package/dist/cjs/cli/lib/ai/config.js +0 -144
  568. package/dist/cjs/cli/lib/ai/config.js.map +0 -7
  569. package/dist/cjs/cli/lib/ai/index.js +0 -704
  570. package/dist/cjs/cli/lib/ai/index.js.map +0 -7
  571. package/dist/cjs/cli/lib/ai/paths.js.map +0 -7
  572. package/dist/cjs-types/cli/lib/ai/config.d.ts +0 -50
  573. package/dist/cjs-types/cli/lib/ai/config.d.ts.map +0 -1
  574. package/dist/cjs-types/cli/lib/ai/config.test.d.ts +0 -2
  575. package/dist/cjs-types/cli/lib/ai/config.test.d.ts.map +0 -1
  576. package/dist/cjs-types/cli/lib/ai/index.d.ts +0 -56
  577. package/dist/cjs-types/cli/lib/ai/index.d.ts.map +0 -1
  578. package/dist/cjs-types/cli/lib/ai/index.test.d.ts.map +0 -1
  579. package/dist/cjs-types/cli/lib/ai/integration.test.d.ts.map +0 -1
  580. package/dist/cjs-types/cli/lib/ai/paths.d.ts.map +0 -1
  581. package/dist/cjs-types/cli/lib/ai/prompt.test.d.ts.map +0 -1
  582. package/dist/esm/cli/lib/ai/config.js +0 -109
  583. package/dist/esm/cli/lib/ai/config.js.map +0 -7
  584. package/dist/esm/cli/lib/ai/index.js +0 -684
  585. package/dist/esm/cli/lib/ai/index.js.map +0 -7
  586. package/dist/esm/cli/lib/ai/paths.js.map +0 -7
  587. package/dist/esm-types/cli/lib/ai/config.d.ts +0 -50
  588. package/dist/esm-types/cli/lib/ai/config.d.ts.map +0 -1
  589. package/dist/esm-types/cli/lib/ai/config.test.d.ts +0 -2
  590. package/dist/esm-types/cli/lib/ai/config.test.d.ts.map +0 -1
  591. package/dist/esm-types/cli/lib/ai/index.d.ts +0 -56
  592. package/dist/esm-types/cli/lib/ai/index.d.ts.map +0 -1
  593. package/dist/esm-types/cli/lib/ai/index.test.d.ts.map +0 -1
  594. package/dist/esm-types/cli/lib/ai/integration.test.d.ts.map +0 -1
  595. package/dist/esm-types/cli/lib/ai/paths.d.ts.map +0 -1
  596. package/dist/esm-types/cli/lib/ai/prompt.test.d.ts.map +0 -1
  597. package/src/cli/lib/ai/config.test.ts +0 -338
  598. package/src/cli/lib/ai/config.ts +0 -159
  599. package/src/cli/lib/ai/index.ts +0 -1006
  600. /package/dist/cjs/cli/lib/{ai → aiFiles}/paths.js +0 -0
  601. /package/dist/cjs-types/cli/lib/{ai → aiFiles}/index.test.d.ts +0 -0
  602. /package/dist/cjs-types/cli/lib/{ai → aiFiles}/integration.test.d.ts +0 -0
  603. /package/dist/cjs-types/cli/lib/{ai → aiFiles}/prompt.test.d.ts +0 -0
  604. /package/dist/esm/cli/lib/{ai → aiFiles}/paths.js +0 -0
  605. /package/dist/esm-types/cli/lib/{ai → aiFiles}/index.test.d.ts +0 -0
  606. /package/dist/esm-types/cli/lib/{ai → aiFiles}/integration.test.d.ts +0 -0
  607. /package/dist/esm-types/cli/lib/{ai → aiFiles}/prompt.test.d.ts +0 -0
@@ -1,11 +1,10 @@
1
1
  import { describe, test, expect, vi, beforeEach, afterEach } from "vitest";
2
- import * as Sentry from "@sentry/node";
3
2
  import { logMessage } from "../../../bundler/log.js";
4
3
  import {
5
- readAiConfig,
6
- writeAiConfig,
7
- writeAiDisabledToProjectConfig,
8
- } from "./config.js";
4
+ attemptReadAiState,
5
+ readAiStateOrDefault,
6
+ writeAiState,
7
+ } from "./state.js";
9
8
  import {
10
9
  downloadGuidelines,
11
10
  fetchAgentSkillsSha,
@@ -15,14 +14,11 @@ import fs from "fs";
15
14
  import os from "os";
16
15
  import path from "path";
17
16
  import {
18
- injectAgentsMdSection,
19
- injectClaudeMdSection,
20
- checkAiFilesStaleness,
21
- updateAiFiles,
17
+ checkAiFilesStalenessAndLog,
18
+ installAiFiles,
22
19
  removeAiFiles,
23
- disableAiFiles,
24
- statusAiFiles,
25
20
  } from "./index.js";
21
+ import { statusAiFiles } from "./status.js";
26
22
  import {
27
23
  AGENTS_MD_START_MARKER,
28
24
  AGENTS_MD_END_MARKER,
@@ -33,147 +29,7 @@ import {
33
29
  } from "../../codegen_templates/claudemd.js";
34
30
 
35
31
  // ---------------------------------------------------------------------------
36
- // injectAgentsMdSection — tested with real temp directories to exercise the
37
- // actual file I/O and string-surgery logic without complex mock wiring.
38
- // ---------------------------------------------------------------------------
39
-
40
- describe("injectAgentsMdSection", () => {
41
- let tmpDir: string;
42
-
43
- beforeEach(() => {
44
- tmpDir = fs.mkdtempSync(`${os.tmpdir()}${path.sep}`);
45
- });
46
-
47
- afterEach(() => {
48
- fs.rmSync(tmpDir, { recursive: true, force: true });
49
- });
50
-
51
- const section = `${AGENTS_MD_START_MARKER}\n## Convex\nRead guidelines.\n${AGENTS_MD_END_MARKER}`;
52
-
53
- test("creates AGENTS.md when it does not exist", async () => {
54
- await injectAgentsMdSection(section, tmpDir);
55
-
56
- const content = fs.readFileSync(path.join(tmpDir, "AGENTS.md"), "utf8");
57
- expect(content).toContain(AGENTS_MD_START_MARKER);
58
- expect(content).toContain(AGENTS_MD_END_MARKER);
59
- expect(content).toContain("## Convex");
60
- });
61
-
62
- test("appends to an existing AGENTS.md that has no Convex section", async () => {
63
- const existing = "# My project\n\nSome existing content.\n";
64
- fs.writeFileSync(path.join(tmpDir, "AGENTS.md"), existing);
65
-
66
- await injectAgentsMdSection(section, tmpDir);
67
-
68
- const content = fs.readFileSync(path.join(tmpDir, "AGENTS.md"), "utf8");
69
- expect(content).toContain("# My project");
70
- expect(content).toContain("Some existing content.");
71
- expect(content).toContain(AGENTS_MD_START_MARKER);
72
- expect(content).toContain("## Convex");
73
- });
74
-
75
- test("replaces an existing Convex section when markers are present", async () => {
76
- const oldSection = `${AGENTS_MD_START_MARKER}\n## Convex\nOld content.\n${AGENTS_MD_END_MARKER}`;
77
- const existing = `# My project\n\n${oldSection}\n`;
78
- fs.writeFileSync(path.join(tmpDir, "AGENTS.md"), existing);
79
-
80
- const newSection = `${AGENTS_MD_START_MARKER}\n## Convex\nNew content.\n${AGENTS_MD_END_MARKER}`;
81
- await injectAgentsMdSection(newSection, tmpDir);
82
-
83
- const content = fs.readFileSync(path.join(tmpDir, "AGENTS.md"), "utf8");
84
- expect(content).toContain("New content.");
85
- expect(content).not.toContain("Old content.");
86
- // Only one occurrence of the start marker
87
- expect(content.split(AGENTS_MD_START_MARKER).length - 1).toBe(1);
88
- });
89
-
90
- test("preserves content before and after an existing Convex section", async () => {
91
- const oldSection = `${AGENTS_MD_START_MARKER}\n## Convex\nOld.\n${AGENTS_MD_END_MARKER}`;
92
- const existing = `# Before\n\n${oldSection}\n\n# After\n`;
93
- fs.writeFileSync(path.join(tmpDir, "AGENTS.md"), existing);
94
-
95
- await injectAgentsMdSection(section, tmpDir);
96
-
97
- const content = fs.readFileSync(path.join(tmpDir, "AGENTS.md"), "utf8");
98
- expect(content).toContain("# Before");
99
- expect(content).toContain("# After");
100
- });
101
-
102
- test("returns a non-null hash of the written content", async () => {
103
- const hash = await injectAgentsMdSection(section, tmpDir);
104
- expect(typeof hash).toBe("string");
105
- expect(hash!.length).toBeGreaterThan(0);
106
- });
107
-
108
- test("returns hash of the section content, not the entire file", async () => {
109
- fs.writeFileSync(
110
- path.join(tmpDir, "AGENTS.md"),
111
- "# My project\n\nExisting content.\n",
112
- );
113
-
114
- const hash = await injectAgentsMdSection(section, tmpDir);
115
-
116
- const { hashSha256 } = await import("../utils/hash.js");
117
- expect(hash).toBe(hashSha256(section));
118
- });
119
- });
120
-
121
- describe("injectClaudeMdSection", () => {
122
- let tmpDir: string;
123
-
124
- beforeEach(() => {
125
- tmpDir = fs.mkdtempSync(`${os.tmpdir()}${path.sep}`);
126
- });
127
-
128
- afterEach(() => {
129
- fs.rmSync(tmpDir, { recursive: true, force: true });
130
- });
131
-
132
- const section = `${CLAUDE_MD_START_MARKER}\n## Convex\nRead guidelines.\n${CLAUDE_MD_END_MARKER}`;
133
-
134
- test("creates CLAUDE.md when it does not exist", async () => {
135
- const result = await injectClaudeMdSection(section, tmpDir);
136
-
137
- const content = fs.readFileSync(path.join(tmpDir, "CLAUDE.md"), "utf8");
138
- expect(content).toContain(CLAUDE_MD_START_MARKER);
139
- expect(content).toContain(CLAUDE_MD_END_MARKER);
140
- expect(result.didWrite).toBe(true);
141
- });
142
-
143
- test("appends managed section to existing CLAUDE.md content", async () => {
144
- fs.writeFileSync(
145
- path.join(tmpDir, "CLAUDE.md"),
146
- "My custom instructions\n",
147
- );
148
-
149
- const result = await injectClaudeMdSection(section, tmpDir);
150
-
151
- const content = fs.readFileSync(path.join(tmpDir, "CLAUDE.md"), "utf8");
152
- expect(content).toContain("My custom instructions");
153
- expect(content).toContain(CLAUDE_MD_START_MARKER);
154
- expect(result.didWrite).toBe(true);
155
- });
156
-
157
- test("replaces managed section without touching user content", async () => {
158
- const oldSection = `${CLAUDE_MD_START_MARKER}\nOld\n${CLAUDE_MD_END_MARKER}`;
159
- fs.writeFileSync(
160
- path.join(tmpDir, "CLAUDE.md"),
161
- `# Header\n\n${oldSection}\n\n# Footer\n`,
162
- "utf8",
163
- );
164
-
165
- await injectClaudeMdSection(section, tmpDir);
166
-
167
- const content = fs.readFileSync(path.join(tmpDir, "CLAUDE.md"), "utf8");
168
- expect(content).toContain("# Header");
169
- expect(content).toContain("# Footer");
170
- expect(content).toContain("## Convex");
171
- expect(content).not.toContain("Old");
172
- });
173
- });
174
-
175
- // ---------------------------------------------------------------------------
176
- // checkAiFilesStaleness — mock-based: logic only, no real I/O needed.
32
+ // Mocks
177
33
  // ---------------------------------------------------------------------------
178
34
 
179
35
  vi.mock("@sentry/node", () => ({
@@ -185,10 +41,11 @@ vi.mock("../../../bundler/log.js", () => ({
185
41
  logMessage: vi.fn(),
186
42
  }));
187
43
 
188
- vi.mock("./config.js", () => ({
189
- readAiConfig: vi.fn(),
190
- writeAiConfig: vi.fn(),
191
- writeAiDisabledToProjectConfig: vi.fn(),
44
+ vi.mock("./state.js", () => ({
45
+ attemptReadAiState: vi.fn(),
46
+ readAiStateOrDefault: vi.fn(),
47
+ writeAiState: vi.fn(),
48
+ hasAiState: vi.fn().mockResolvedValue(false),
192
49
  }));
193
50
 
194
51
  vi.mock("../versionApi.js", () => ({
@@ -201,7 +58,6 @@ vi.mock("child_process", () => ({
201
58
  default: {
202
59
  spawn: vi.fn(() => {
203
60
  const emitter = { on: vi.fn() };
204
- // Immediately simulate a successful exit.
205
61
  emitter.on.mockImplementation(
206
62
  (event: string, cb: (arg: number) => void) => {
207
63
  if (event === "close") cb(0);
@@ -213,28 +69,30 @@ vi.mock("child_process", () => ({
213
69
  }));
214
70
 
215
71
  const mockLogMessage = vi.mocked(logMessage);
216
- const mockReadAiConfig = vi.mocked(readAiConfig);
217
- const mockWriteAiConfig = vi.mocked(writeAiConfig);
218
- const mockWriteAiDisabledToProjectConfig = vi.mocked(
219
- writeAiDisabledToProjectConfig,
220
- );
72
+ const mockAttemptReadAiState = vi.mocked(attemptReadAiState);
73
+ const mockReadAiStateOrDefault = vi.mocked(readAiStateOrDefault);
74
+ const mockWriteAiState = vi.mocked(writeAiState);
221
75
  const mockDownloadGuidelines = vi.mocked(downloadGuidelines);
222
76
  const mockFetchAgentSkillsSha = vi.mocked(fetchAgentSkillsSha);
223
77
  const mockGetVersion = vi.mocked(getVersion);
224
- const mockCaptureException = vi.mocked(Sentry.captureException);
225
78
 
226
- /** Minimal valid config used across tests; includes all required fields. */
227
- const baseConfig = {
79
+ /** Minimal valid state used across tests; includes all required fields. */
80
+ const baseState = {
228
81
  guidelinesHash: null,
229
82
  agentsMdSectionHash: null,
230
83
  claudeMdHash: null,
231
84
  agentSkillsSha: null,
232
85
  installedSkillNames: [] as string[],
233
- disableStalenessMessage: false,
234
86
  };
235
87
 
88
+ // ---------------------------------------------------------------------------
89
+ // checkAiFilesStaleness
90
+ // ---------------------------------------------------------------------------
91
+
236
92
  describe("checkAiFilesStaleness", () => {
237
- beforeEach(() => vi.clearAllMocks());
93
+ beforeEach(() => {
94
+ vi.clearAllMocks();
95
+ });
238
96
  afterEach(() => {
239
97
  vi.unstubAllEnvs();
240
98
  vi.resetAllMocks();
@@ -244,11 +102,16 @@ describe("checkAiFilesStaleness", () => {
244
102
  const dummyConvexDir = "/tmp/test-project/convex";
245
103
 
246
104
  test("logs install nudge when no state file exists, even with null canonical values", async () => {
247
- mockReadAiConfig.mockResolvedValue(null);
105
+ mockAttemptReadAiState.mockResolvedValue({ kind: "no-file" });
248
106
 
249
- await checkAiFilesStaleness(null, null, dummyProjectDir, dummyConvexDir);
107
+ await checkAiFilesStalenessAndLog({
108
+ canonicalGuidelinesHash: null,
109
+ canonicalAgentSkillsSha: null,
110
+ projectDir: dummyProjectDir,
111
+ convexDir: dummyConvexDir,
112
+ });
250
113
 
251
- expect(mockReadAiConfig).toHaveBeenCalled();
114
+ expect(mockAttemptReadAiState).toHaveBeenCalled();
252
115
  expect(mockLogMessage).toHaveBeenCalledWith(
253
116
  expect.stringContaining("npx convex ai-files install"),
254
117
  );
@@ -257,26 +120,31 @@ describe("checkAiFilesStaleness", () => {
257
120
  );
258
121
  });
259
122
 
260
- test("does nothing when both canonical values are null but config exists (version server unavailable)", async () => {
261
- mockReadAiConfig.mockResolvedValue({
262
- ...baseConfig,
263
- guidelinesHash: "some-hash",
123
+ test("does nothing when both canonical values are null but state exists (version server unavailable)", async () => {
124
+ mockAttemptReadAiState.mockResolvedValue({
125
+ kind: "ok",
126
+ state: { ...baseState, guidelinesHash: "some-hash" },
264
127
  });
265
128
 
266
- await checkAiFilesStaleness(null, null, dummyProjectDir, dummyConvexDir);
129
+ await checkAiFilesStalenessAndLog({
130
+ canonicalGuidelinesHash: null,
131
+ canonicalAgentSkillsSha: null,
132
+ projectDir: dummyProjectDir,
133
+ convexDir: dummyConvexDir,
134
+ });
267
135
 
268
136
  expect(mockLogMessage).not.toHaveBeenCalled();
269
137
  });
270
138
 
271
139
  test("logs install nudge when no state file exists, even if canonical hashes are available", async () => {
272
- mockReadAiConfig.mockResolvedValue(null);
140
+ mockAttemptReadAiState.mockResolvedValue({ kind: "no-file" });
273
141
 
274
- await checkAiFilesStaleness(
275
- "canonical-hash",
276
- null,
277
- dummyProjectDir,
278
- dummyConvexDir,
279
- );
142
+ await checkAiFilesStalenessAndLog({
143
+ canonicalGuidelinesHash: "canonical-hash",
144
+ canonicalAgentSkillsSha: null,
145
+ projectDir: dummyProjectDir,
146
+ convexDir: dummyConvexDir,
147
+ });
280
148
 
281
149
  expect(mockLogMessage).toHaveBeenCalledWith(
282
150
  expect.stringContaining("npx convex ai-files install"),
@@ -289,72 +157,48 @@ describe("checkAiFilesStaleness", () => {
289
157
  );
290
158
  });
291
159
 
292
- test("adds agent-specific guidance when in agent mode", async () => {
293
- vi.stubEnv("CONVEX_AGENT_MODE", "anonymous");
294
- mockReadAiConfig.mockResolvedValue(null);
295
-
296
- await checkAiFilesStaleness(
297
- "canonical-hash",
298
- null,
299
- dummyProjectDir,
300
- dummyConvexDir,
301
- );
302
-
303
- expect(mockLogMessage).toHaveBeenCalledWith(
304
- expect.stringContaining("If you are an agent tell the human to run"),
305
- );
306
- expect(mockLogMessage).toHaveBeenCalledWith(
307
- expect.stringContaining("npx convex ai-files install"),
308
- );
309
- expect(mockLogMessage).toHaveBeenCalledWith(
310
- expect.stringContaining("npx convex ai-files disable"),
311
- );
312
- });
160
+ test("does nothing when config has enabled=false (user opted out)", async () => {
161
+ mockAttemptReadAiState.mockResolvedValue({ kind: "no-file" });
313
162
 
314
- test("does nothing when config has disableStalenessMessage=true (user opted out)", async () => {
315
- mockReadAiConfig.mockResolvedValue({
316
- ...baseConfig,
317
- disableStalenessMessage: true,
163
+ await checkAiFilesStalenessAndLog({
164
+ canonicalGuidelinesHash: "canonical-hash",
165
+ canonicalAgentSkillsSha: null,
166
+ aiFilesConfig: { enabled: false },
167
+ projectDir: dummyProjectDir,
168
+ convexDir: dummyConvexDir,
318
169
  });
319
170
 
320
- await checkAiFilesStaleness(
321
- "canonical-hash",
322
- null,
323
- dummyProjectDir,
324
- dummyConvexDir,
325
- );
326
-
327
171
  expect(mockLogMessage).not.toHaveBeenCalled();
328
172
  });
329
173
 
330
174
  test("does nothing when stored guidelines hash matches canonical", async () => {
331
- mockReadAiConfig.mockResolvedValue({
332
- ...baseConfig,
333
- guidelinesHash: "same-hash",
175
+ mockAttemptReadAiState.mockResolvedValue({
176
+ kind: "ok",
177
+ state: { ...baseState, guidelinesHash: "same-hash" },
334
178
  });
335
179
 
336
- await checkAiFilesStaleness(
337
- "same-hash",
338
- null,
339
- dummyProjectDir,
340
- dummyConvexDir,
341
- );
180
+ await checkAiFilesStalenessAndLog({
181
+ canonicalGuidelinesHash: "same-hash",
182
+ canonicalAgentSkillsSha: null,
183
+ projectDir: dummyProjectDir,
184
+ convexDir: dummyConvexDir,
185
+ });
342
186
 
343
187
  expect(mockLogMessage).not.toHaveBeenCalled();
344
188
  });
345
189
 
346
190
  test("logs nag message when guidelines hash is stale", async () => {
347
- mockReadAiConfig.mockResolvedValue({
348
- ...baseConfig,
349
- guidelinesHash: "old-hash",
191
+ mockAttemptReadAiState.mockResolvedValue({
192
+ kind: "ok",
193
+ state: { ...baseState, guidelinesHash: "old-hash" },
350
194
  });
351
195
 
352
- await checkAiFilesStaleness(
353
- "new-canonical-hash",
354
- null,
355
- dummyProjectDir,
356
- dummyConvexDir,
357
- );
196
+ await checkAiFilesStalenessAndLog({
197
+ canonicalGuidelinesHash: "new-canonical-hash",
198
+ canonicalAgentSkillsSha: null,
199
+ projectDir: dummyProjectDir,
200
+ convexDir: dummyConvexDir,
201
+ });
358
202
 
359
203
  expect(mockLogMessage).toHaveBeenCalledWith(
360
204
  expect.stringContaining("npx convex ai-files update"),
@@ -362,18 +206,21 @@ describe("checkAiFilesStaleness", () => {
362
206
  });
363
207
 
364
208
  test("logs nag message when agent skills SHA is stale", async () => {
365
- mockReadAiConfig.mockResolvedValue({
366
- ...baseConfig,
367
- guidelinesHash: "current-hash",
368
- agentSkillsSha: "old-sha",
209
+ mockAttemptReadAiState.mockResolvedValue({
210
+ kind: "ok",
211
+ state: {
212
+ ...baseState,
213
+ guidelinesHash: "current-hash",
214
+ agentSkillsSha: "old-sha",
215
+ },
369
216
  });
370
217
 
371
- await checkAiFilesStaleness(
372
- "current-hash",
373
- "new-sha",
374
- dummyProjectDir,
375
- dummyConvexDir,
376
- );
218
+ await checkAiFilesStalenessAndLog({
219
+ canonicalGuidelinesHash: "current-hash",
220
+ canonicalAgentSkillsSha: "new-sha",
221
+ projectDir: dummyProjectDir,
222
+ convexDir: dummyConvexDir,
223
+ });
377
224
 
378
225
  expect(mockLogMessage).toHaveBeenCalledWith(
379
226
  expect.stringContaining("npx convex ai-files update"),
@@ -381,38 +228,44 @@ describe("checkAiFilesStaleness", () => {
381
228
  });
382
229
 
383
230
  test("does nothing when stored guidelinesHash is null (never written)", async () => {
384
- mockReadAiConfig.mockResolvedValue(baseConfig);
231
+ mockAttemptReadAiState.mockResolvedValue({
232
+ kind: "ok",
233
+ state: baseState,
234
+ });
385
235
 
386
- await checkAiFilesStaleness(
387
- "some-hash",
388
- "some-sha",
389
- dummyProjectDir,
390
- dummyConvexDir,
391
- );
236
+ await checkAiFilesStalenessAndLog({
237
+ canonicalGuidelinesHash: "some-hash",
238
+ canonicalAgentSkillsSha: "some-sha",
239
+ projectDir: dummyProjectDir,
240
+ convexDir: dummyConvexDir,
241
+ });
392
242
 
393
243
  expect(mockLogMessage).not.toHaveBeenCalled();
394
244
  });
395
245
  });
396
246
 
397
247
  // ---------------------------------------------------------------------------
398
- // updateAiFiles — mock-based.
248
+ // installAiFiles
399
249
  // ---------------------------------------------------------------------------
400
250
 
401
- describe("updateAiFiles", () => {
251
+ describe("installAiFiles", () => {
402
252
  beforeEach(() => {
403
253
  vi.clearAllMocks();
404
254
  mockFetchAgentSkillsSha.mockResolvedValue("canonical-sha-abc123");
405
255
  mockGetVersion.mockResolvedValue({
406
- message: null,
407
- guidelinesHash: null,
408
- agentSkillsSha: "canonical-sha-abc123",
409
- disableSkillsCli: false,
256
+ kind: "ok",
257
+ data: {
258
+ message: null,
259
+ guidelinesHash: null,
260
+ agentSkillsSha: "canonical-sha-abc123",
261
+ disableSkillsCli: false,
262
+ },
410
263
  });
411
264
  });
412
265
  afterEach(() => vi.resetAllMocks());
413
266
 
414
- test("runs full init and installs skills when no config exists", async () => {
415
- mockReadAiConfig.mockResolvedValue(null);
267
+ test("runs full init and installs skills when no state exists", async () => {
268
+ mockReadAiStateOrDefault.mockResolvedValue(baseState);
416
269
 
417
270
  const tmpDir = fs.mkdtempSync(`${os.tmpdir()}${path.sep}`);
418
271
  const convexDir = path.join(tmpDir, "convex");
@@ -422,7 +275,7 @@ describe("updateAiFiles", () => {
422
275
 
423
276
  mockDownloadGuidelines.mockResolvedValue("guidelines content");
424
277
 
425
- await updateAiFiles(tmpDir, convexDir);
278
+ await installAiFiles({ projectDir: tmpDir, convexDir });
426
279
 
427
280
  expect(
428
281
  fs.existsSync(
@@ -441,32 +294,6 @@ describe("updateAiFiles", () => {
441
294
  }
442
295
  });
443
296
 
444
- test("reports up to date when guidelines hash already matches", async () => {
445
- mockDownloadGuidelines.mockResolvedValue("guidelines content");
446
- const tmpDir = fs.mkdtempSync(`${os.tmpdir()}${path.sep}`);
447
- const convexDir = path.join(tmpDir, "convex");
448
-
449
- const { hashSha256 } = await import("../utils/hash.js");
450
- const realHash = hashSha256("guidelines content");
451
-
452
- mockReadAiConfig.mockResolvedValue({
453
- ...baseConfig,
454
- guidelinesHash: realHash,
455
- agentSkillsSha: "canonical-sha-abc123",
456
- });
457
-
458
- try {
459
- await updateAiFiles(tmpDir, convexDir);
460
-
461
- expect(mockLogMessage).toHaveBeenCalledWith(
462
- expect.stringContaining("already up to date"),
463
- );
464
- expect(mockCaptureException).not.toHaveBeenCalled();
465
- } finally {
466
- fs.rmSync(tmpDir, { recursive: true, force: true });
467
- }
468
- });
469
-
470
297
  test("stores canonical agentSkillsSha and skill names after successful install", async () => {
471
298
  const tmpDir = fs.mkdtempSync(`${os.tmpdir()}${path.sep}`);
472
299
  const convexDir = path.join(tmpDir, "convex");
@@ -485,79 +312,20 @@ describe("updateAiFiles", () => {
485
312
  }
486
313
 
487
314
  mockDownloadGuidelines.mockResolvedValue(null);
488
- mockReadAiConfig.mockResolvedValue({
489
- ...baseConfig,
315
+ mockReadAiStateOrDefault.mockResolvedValue({
316
+ ...baseState,
490
317
  agentSkillsSha: "old-sha",
491
318
  });
492
319
 
493
- await updateAiFiles(tmpDir, convexDir);
494
-
495
- expect(mockWriteAiConfig).toHaveBeenCalledWith(
496
- expect.objectContaining({
497
- agentSkillsSha: "canonical-sha-abc123",
498
- installedSkillNames: ["migration-helper", "schema-builder"],
499
- }),
500
- expect.anything(),
501
- expect.anything(),
502
- );
503
- } finally {
504
- fs.rmSync(tmpDir, { recursive: true, force: true });
505
- }
506
- });
507
-
508
- test("update does not clear disableStalenessMessage when set true", async () => {
509
- const tmpDir = fs.mkdtempSync(`${os.tmpdir()}${path.sep}`);
510
- const convexDir = path.join(tmpDir, "convex");
511
- try {
512
- mockReadAiConfig.mockResolvedValue({
513
- ...baseConfig,
514
- disableStalenessMessage: true,
515
- });
516
- mockDownloadGuidelines.mockResolvedValue(null);
517
-
518
- await updateAiFiles(tmpDir, convexDir);
519
-
520
- expect(mockWriteAiConfig).toHaveBeenCalledWith(
521
- expect.objectContaining({ disableStalenessMessage: true }),
522
- expect.anything(),
523
- expect.anything(),
524
- );
525
- } finally {
526
- fs.rmSync(tmpDir, { recursive: true, force: true });
527
- }
528
- });
529
-
530
- test("update recreates convex/_generated/ai when only disable config exists", async () => {
531
- const tmpDir = fs.mkdtempSync(`${os.tmpdir()}${path.sep}`);
532
- const convexDir = path.join(tmpDir, "convex");
533
- try {
534
- fs.mkdirSync(convexDir, { recursive: true });
535
- fs.writeFileSync(path.join(convexDir, "schema.ts"), "");
536
- fs.writeFileSync(path.join(tmpDir, "convex.json"), "{}");
537
- mockReadAiConfig.mockResolvedValue({
538
- ...baseConfig,
539
- disableStalenessMessage: true,
540
- guidelinesHash: null,
541
- });
542
- mockDownloadGuidelines.mockResolvedValue("fresh guidelines");
543
-
544
- await updateAiFiles(tmpDir, convexDir);
320
+ await installAiFiles({ projectDir: tmpDir, convexDir });
545
321
 
546
- expect(fs.existsSync(path.join(convexDir, "_generated", "ai"))).toBe(
547
- true,
548
- );
549
- expect(
550
- fs.existsSync(
551
- path.join(convexDir, "_generated", "ai", "guidelines.md"),
552
- ),
553
- ).toBe(true);
554
- expect(mockWriteAiConfig).toHaveBeenCalledWith(
322
+ expect(mockWriteAiState).toHaveBeenCalledWith(
555
323
  expect.objectContaining({
556
- disableStalenessMessage: true,
557
- guidelinesHash: expect.any(String),
324
+ state: expect.objectContaining({
325
+ agentSkillsSha: "canonical-sha-abc123",
326
+ installedSkillNames: ["migration-helper", "schema-builder"],
327
+ }),
558
328
  }),
559
- tmpDir,
560
- expect.anything(),
561
329
  );
562
330
  } finally {
563
331
  fs.rmSync(tmpDir, { recursive: true, force: true });
@@ -568,10 +336,10 @@ describe("updateAiFiles", () => {
568
336
  const tmpDir = fs.mkdtempSync(`${os.tmpdir()}${path.sep}`);
569
337
  const convexDir = path.join(tmpDir, "convex");
570
338
  try {
571
- mockReadAiConfig.mockResolvedValue(baseConfig);
339
+ mockReadAiStateOrDefault.mockResolvedValue(baseState);
572
340
  mockDownloadGuidelines.mockResolvedValue(null);
573
341
 
574
- await updateAiFiles(tmpDir, convexDir);
342
+ await installAiFiles({ projectDir: tmpDir, convexDir });
575
343
 
576
344
  expect(mockLogMessage).toHaveBeenCalledWith(
577
345
  expect.stringContaining("Could not download Convex AI guidelines"),
@@ -585,16 +353,19 @@ describe("updateAiFiles", () => {
585
353
  const tmpDir = fs.mkdtempSync(`${os.tmpdir()}${path.sep}`);
586
354
  const convexDir = path.join(tmpDir, "convex");
587
355
  try {
588
- mockReadAiConfig.mockResolvedValue(baseConfig);
356
+ mockReadAiStateOrDefault.mockResolvedValue(baseState);
589
357
  mockDownloadGuidelines.mockResolvedValue("guidelines content");
590
358
  mockGetVersion.mockResolvedValue({
591
- message: null,
592
- guidelinesHash: null,
593
- agentSkillsSha: null,
594
- disableSkillsCli: true,
359
+ kind: "ok",
360
+ data: {
361
+ message: null,
362
+ guidelinesHash: null,
363
+ agentSkillsSha: null,
364
+ disableSkillsCli: true,
365
+ },
595
366
  });
596
367
 
597
- await updateAiFiles(tmpDir, convexDir);
368
+ await installAiFiles({ projectDir: tmpDir, convexDir });
598
369
 
599
370
  const { default: cp } = await import("child_process");
600
371
  const spawnCalls = vi.mocked(cp.spawn).mock.calls;
@@ -612,7 +383,7 @@ describe("updateAiFiles", () => {
612
383
  });
613
384
 
614
385
  // ---------------------------------------------------------------------------
615
- // removeAiFiles — tested with real temp directories.
386
+ // removeAiFiles
616
387
  // ---------------------------------------------------------------------------
617
388
 
618
389
  describe("removeAiFiles", () => {
@@ -622,10 +393,13 @@ describe("removeAiFiles", () => {
622
393
  beforeEach(() => {
623
394
  vi.clearAllMocks();
624
395
  mockGetVersion.mockResolvedValue({
625
- message: null,
626
- guidelinesHash: null,
627
- agentSkillsSha: "canonical-sha-abc123",
628
- disableSkillsCli: false,
396
+ kind: "ok",
397
+ data: {
398
+ message: null,
399
+ guidelinesHash: null,
400
+ agentSkillsSha: "canonical-sha-abc123",
401
+ disableSkillsCli: false,
402
+ },
629
403
  });
630
404
  tmpDir = fs.mkdtempSync(`${os.tmpdir()}${path.sep}`);
631
405
  convexDir = path.join(tmpDir, "convex");
@@ -639,41 +413,47 @@ describe("removeAiFiles", () => {
639
413
  vi.resetAllMocks();
640
414
  });
641
415
 
642
- function writeConfig(override: Partial<typeof baseConfig> = {}) {
643
- const config = { ...baseConfig, ...override };
644
- fs.writeFileSync(
645
- path.join(tmpDir, "convex", "_generated", "ai", "ai-files.state.json"),
646
- JSON.stringify(config, null, 2) + "\n",
647
- "utf8",
416
+ test("logs nothing-to-remove when no state and no artifacts exist", async () => {
417
+ mockAttemptReadAiState.mockResolvedValue({ kind: "no-file" });
418
+ fs.rmSync(path.join(convexDir, "_generated", "ai"), { recursive: true });
419
+
420
+ await removeAiFiles({ projectDir: tmpDir, convexDir });
421
+
422
+ expect(mockLogMessage).toHaveBeenCalledWith(
423
+ expect.stringContaining("nothing to remove"),
648
424
  );
649
- }
425
+ });
650
426
 
651
- test("logs nothing-to-remove when no config exists", async () => {
652
- // No config file written — readAiConfig returns null.
653
- mockReadAiConfig.mockResolvedValue(null);
427
+ test("removes ai dir even when no state file exists", async () => {
428
+ mockAttemptReadAiState.mockResolvedValue({ kind: "no-file" });
654
429
 
655
- await removeAiFiles(tmpDir, convexDir);
430
+ await removeAiFiles({ projectDir: tmpDir, convexDir });
656
431
 
657
432
  expect(mockLogMessage).toHaveBeenCalledWith(
658
- expect.stringContaining("nothing to remove"),
433
+ expect.stringContaining("Convex AI files removed"),
659
434
  );
435
+ expect(fs.existsSync(path.join(convexDir, "_generated", "ai"))).toBe(false);
660
436
  });
661
437
 
662
438
  test("deletes AGENTS.md if stripping the Convex section leaves it empty", async () => {
663
- writeConfig();
664
- mockReadAiConfig.mockResolvedValue(baseConfig);
439
+ mockAttemptReadAiState.mockResolvedValue({
440
+ kind: "ok",
441
+ state: baseState,
442
+ });
665
443
 
666
444
  const agentsMdContent = `${AGENTS_MD_START_MARKER}\n## Convex\nGuidelines.\n${AGENTS_MD_END_MARKER}\n`;
667
445
  fs.writeFileSync(path.join(tmpDir, "AGENTS.md"), agentsMdContent, "utf8");
668
446
 
669
- await removeAiFiles(tmpDir, convexDir);
447
+ await removeAiFiles({ projectDir: tmpDir, convexDir });
670
448
 
671
449
  expect(fs.existsSync(path.join(tmpDir, "AGENTS.md"))).toBe(false);
672
450
  });
673
451
 
674
452
  test("strips Convex section from AGENTS.md", async () => {
675
- writeConfig();
676
- mockReadAiConfig.mockResolvedValue(baseConfig);
453
+ mockAttemptReadAiState.mockResolvedValue({
454
+ kind: "ok",
455
+ state: baseState,
456
+ });
677
457
 
678
458
  const agentsMdContent =
679
459
  `# My project\n\n` +
@@ -681,7 +461,7 @@ describe("removeAiFiles", () => {
681
461
  `# After\n`;
682
462
  fs.writeFileSync(path.join(tmpDir, "AGENTS.md"), agentsMdContent, "utf8");
683
463
 
684
- await removeAiFiles(tmpDir, convexDir);
464
+ await removeAiFiles({ projectDir: tmpDir, convexDir });
685
465
 
686
466
  const result = fs.readFileSync(path.join(tmpDir, "AGENTS.md"), "utf8");
687
467
  expect(result).toContain("# My project");
@@ -691,30 +471,36 @@ describe("removeAiFiles", () => {
691
471
  });
692
472
 
693
473
  test("deletes CLAUDE.md when it only contains the managed section", async () => {
694
- writeConfig();
695
- mockReadAiConfig.mockResolvedValue(baseConfig);
474
+ mockAttemptReadAiState.mockResolvedValue({
475
+ kind: "ok",
476
+ state: baseState,
477
+ });
696
478
  const managed = `${CLAUDE_MD_START_MARKER}\n## Convex\nRead guidelines.\n${CLAUDE_MD_END_MARKER}\n`;
697
479
  fs.writeFileSync(path.join(tmpDir, "CLAUDE.md"), managed, "utf8");
698
480
 
699
- await removeAiFiles(tmpDir, convexDir);
481
+ await removeAiFiles({ projectDir: tmpDir, convexDir });
700
482
 
701
483
  expect(fs.existsSync(path.join(tmpDir, "CLAUDE.md"))).toBe(false);
702
484
  });
703
485
 
704
486
  test("leaves CLAUDE.md when it has no managed markers", async () => {
705
- writeConfig();
706
- mockReadAiConfig.mockResolvedValue(baseConfig);
487
+ mockAttemptReadAiState.mockResolvedValue({
488
+ kind: "ok",
489
+ state: baseState,
490
+ });
707
491
 
708
492
  fs.writeFileSync(path.join(tmpDir, "CLAUDE.md"), "User content\n", "utf8");
709
493
 
710
- await removeAiFiles(tmpDir, convexDir);
494
+ await removeAiFiles({ projectDir: tmpDir, convexDir });
711
495
 
712
496
  expect(fs.existsSync(path.join(tmpDir, "CLAUDE.md"))).toBe(true);
713
497
  });
714
498
 
715
499
  test("strips only the Convex section from CLAUDE.md", async () => {
716
- writeConfig();
717
- mockReadAiConfig.mockResolvedValue(baseConfig);
500
+ mockAttemptReadAiState.mockResolvedValue({
501
+ kind: "ok",
502
+ state: baseState,
503
+ });
718
504
  const managed = `${CLAUDE_MD_START_MARKER}\n## Convex\nRead guidelines.\n${CLAUDE_MD_END_MARKER}`;
719
505
  fs.writeFileSync(
720
506
  path.join(tmpDir, "CLAUDE.md"),
@@ -722,7 +508,7 @@ describe("removeAiFiles", () => {
722
508
  "utf8",
723
509
  );
724
510
 
725
- await removeAiFiles(tmpDir, convexDir);
511
+ await removeAiFiles({ projectDir: tmpDir, convexDir });
726
512
 
727
513
  const content = fs.readFileSync(path.join(tmpDir, "CLAUDE.md"), "utf8");
728
514
  expect(content).toContain("# User header");
@@ -732,9 +518,9 @@ describe("removeAiFiles", () => {
732
518
  });
733
519
 
734
520
  test("leaves CLAUDE.md alone when it has no managed markers (legacy)", async () => {
735
- mockReadAiConfig.mockResolvedValue({
736
- ...baseConfig,
737
- claudeMdHash: "some-hash",
521
+ mockAttemptReadAiState.mockResolvedValue({
522
+ kind: "ok",
523
+ state: { ...baseState, claudeMdHash: "some-hash" },
738
524
  });
739
525
 
740
526
  fs.writeFileSync(
@@ -743,7 +529,7 @@ describe("removeAiFiles", () => {
743
529
  "utf8",
744
530
  );
745
531
 
746
- await removeAiFiles(tmpDir, convexDir);
532
+ await removeAiFiles({ projectDir: tmpDir, convexDir });
747
533
 
748
534
  expect(fs.existsSync(path.join(tmpDir, "CLAUDE.md"))).toBe(true);
749
535
  expect(fs.readFileSync(path.join(tmpDir, "CLAUDE.md"), "utf8")).toBe(
@@ -753,14 +539,13 @@ describe("removeAiFiles", () => {
753
539
 
754
540
  test("calls skills remove for each tracked skill name", async () => {
755
541
  const skillNames = ["migration-helper", "schema-builder"];
756
- mockReadAiConfig.mockResolvedValue({
757
- ...baseConfig,
758
- installedSkillNames: skillNames,
542
+ mockAttemptReadAiState.mockResolvedValue({
543
+ kind: "ok",
544
+ state: { ...baseState, installedSkillNames: skillNames },
759
545
  });
760
546
 
761
- await removeAiFiles(tmpDir, convexDir);
547
+ await removeAiFiles({ projectDir: tmpDir, convexDir });
762
548
 
763
- // child_process.spawn should have been called with the skill names.
764
549
  const { default: cp } = await import("child_process");
765
550
  const spawnCalls = vi.mocked(cp.spawn).mock.calls;
766
551
  const removeCall = spawnCalls.find(
@@ -773,10 +558,9 @@ describe("removeAiFiles", () => {
773
558
 
774
559
  test("deletes skills-lock.json if it becomes empty after removing our skills", async () => {
775
560
  const skillNames = ["migration-helper"];
776
- writeConfig({ installedSkillNames: skillNames });
777
- mockReadAiConfig.mockResolvedValue({
778
- ...baseConfig,
779
- installedSkillNames: skillNames,
561
+ mockAttemptReadAiState.mockResolvedValue({
562
+ kind: "ok",
563
+ state: { ...baseState, installedSkillNames: skillNames },
780
564
  });
781
565
 
782
566
  const lockfileContent = {
@@ -791,17 +575,16 @@ describe("removeAiFiles", () => {
791
575
  "utf8",
792
576
  );
793
577
 
794
- await removeAiFiles(tmpDir, convexDir);
578
+ await removeAiFiles({ projectDir: tmpDir, convexDir });
795
579
 
796
580
  expect(fs.existsSync(path.join(tmpDir, "skills-lock.json"))).toBe(false);
797
581
  });
798
582
 
799
583
  test("preserves skills-lock.json if it contains other skills", async () => {
800
584
  const skillNames = ["migration-helper"];
801
- writeConfig({ installedSkillNames: skillNames });
802
- mockReadAiConfig.mockResolvedValue({
803
- ...baseConfig,
804
- installedSkillNames: skillNames,
585
+ mockAttemptReadAiState.mockResolvedValue({
586
+ kind: "ok",
587
+ state: { ...baseState, installedSkillNames: skillNames },
805
588
  });
806
589
 
807
590
  const lockfileContent = {
@@ -817,25 +600,28 @@ describe("removeAiFiles", () => {
817
600
  "utf8",
818
601
  );
819
602
 
820
- await removeAiFiles(tmpDir, convexDir);
603
+ await removeAiFiles({ projectDir: tmpDir, convexDir });
821
604
 
822
605
  expect(fs.existsSync(path.join(tmpDir, "skills-lock.json"))).toBe(true);
823
606
  });
824
607
 
825
608
  test("skips skills remove when server kill switch is enabled", async () => {
826
609
  const skillNames = ["migration-helper"];
827
- mockReadAiConfig.mockResolvedValue({
828
- ...baseConfig,
829
- installedSkillNames: skillNames,
610
+ mockAttemptReadAiState.mockResolvedValue({
611
+ kind: "ok",
612
+ state: { ...baseState, installedSkillNames: skillNames },
830
613
  });
831
614
  mockGetVersion.mockResolvedValue({
832
- message: null,
833
- guidelinesHash: null,
834
- agentSkillsSha: null,
835
- disableSkillsCli: true,
615
+ kind: "ok",
616
+ data: {
617
+ message: null,
618
+ guidelinesHash: null,
619
+ agentSkillsSha: null,
620
+ disableSkillsCli: true,
621
+ },
836
622
  });
837
623
 
838
- await removeAiFiles(tmpDir, convexDir);
624
+ await removeAiFiles({ projectDir: tmpDir, convexDir });
839
625
 
840
626
  const { default: cp } = await import("child_process");
841
627
  const spawnCalls = vi.mocked(cp.spawn).mock.calls;
@@ -848,51 +634,20 @@ describe("removeAiFiles", () => {
848
634
  );
849
635
  });
850
636
 
851
- test("does NOT write a disabled config after plain remove", async () => {
852
- writeConfig();
853
- mockReadAiConfig.mockResolvedValue(baseConfig);
854
-
855
- await removeAiFiles(tmpDir, convexDir);
856
-
857
- // removeAiFiles should not call writeAiConfig — that is disableAiFiles's job.
858
- expect(mockWriteAiConfig).not.toHaveBeenCalled();
859
- });
860
-
861
- test("disableAiFiles writes disableStalenessMessage=true without removing files", async () => {
862
- writeConfig({ guidelinesHash: null });
863
- mockReadAiConfig.mockResolvedValue(baseConfig);
864
-
865
- fs.writeFileSync(
866
- path.join(convexDir, "_generated", "ai", "guidelines.md"),
867
- "guidelines content",
868
- "utf8",
869
- );
870
-
871
- await disableAiFiles(tmpDir);
872
-
873
- expect(mockWriteAiDisabledToProjectConfig).toHaveBeenCalledWith(
874
- true,
875
- tmpDir,
876
- );
877
- expect(
878
- fs.existsSync(path.join(convexDir, "_generated", "ai", "guidelines.md")),
879
- ).toBe(true);
880
- });
881
-
882
- test("disableAiFiles writes config to project root, not convex dir", async () => {
883
- mockReadAiConfig.mockResolvedValue(null);
637
+ test("does NOT write state after plain remove", async () => {
638
+ mockAttemptReadAiState.mockResolvedValue({
639
+ kind: "ok",
640
+ state: baseState,
641
+ });
884
642
 
885
- await disableAiFiles(tmpDir);
643
+ await removeAiFiles({ projectDir: tmpDir, convexDir });
886
644
 
887
- expect(mockWriteAiDisabledToProjectConfig).toHaveBeenCalledWith(
888
- true,
889
- tmpDir,
890
- );
645
+ expect(mockWriteAiState).not.toHaveBeenCalled();
891
646
  });
892
647
  });
893
648
 
894
649
  // ---------------------------------------------------------------------------
895
- // statusAiFiles — mock-based.
650
+ // statusAiFiles
896
651
  // ---------------------------------------------------------------------------
897
652
 
898
653
  describe("statusAiFiles", () => {
@@ -902,18 +657,24 @@ describe("statusAiFiles", () => {
902
657
  beforeEach(() => {
903
658
  vi.clearAllMocks();
904
659
  mockGetVersion.mockResolvedValue({
905
- message: null,
906
- guidelinesHash: "canonical-guidelines-hash",
907
- agentSkillsSha: "canonical-skills-sha",
908
- disableSkillsCli: false,
660
+ kind: "ok",
661
+ data: {
662
+ message: null,
663
+ guidelinesHash: "canonical-guidelines-hash",
664
+ agentSkillsSha: "canonical-skills-sha",
665
+ disableSkillsCli: false,
666
+ },
909
667
  });
910
668
  });
911
669
  afterEach(() => vi.resetAllMocks());
912
670
 
913
- test("reports not installed when config is null", async () => {
914
- mockReadAiConfig.mockResolvedValue(null);
671
+ test("reports not installed when state is missing", async () => {
672
+ mockAttemptReadAiState.mockResolvedValue({ kind: "no-file" });
915
673
 
916
- await statusAiFiles(dummyProjectDir, dummyConvexDir);
674
+ await statusAiFiles({
675
+ projectDir: dummyProjectDir,
676
+ convexDir: dummyConvexDir,
677
+ });
917
678
 
918
679
  expect(mockLogMessage).toHaveBeenCalledWith(
919
680
  expect.stringContaining("not installed"),
@@ -923,14 +684,13 @@ describe("statusAiFiles", () => {
923
684
  );
924
685
  });
925
686
 
926
- test("reports disabled when config has disableStalenessMessage=true", async () => {
927
- mockReadAiConfig.mockResolvedValue({
928
- ...baseConfig,
929
- disableStalenessMessage: true,
687
+ test("reports disabled when config has enabled=false", async () => {
688
+ await statusAiFiles({
689
+ projectDir: dummyProjectDir,
690
+ convexDir: dummyConvexDir,
691
+ aiFilesConfig: { enabled: false },
930
692
  });
931
693
 
932
- await statusAiFiles(dummyProjectDir, dummyConvexDir);
933
-
934
694
  expect(mockLogMessage).toHaveBeenCalledWith(
935
695
  expect.stringContaining("disabled"),
936
696
  );
@@ -939,10 +699,16 @@ describe("statusAiFiles", () => {
939
699
  );
940
700
  });
941
701
 
942
- test("reports enabled when config exists and disableStalenessMessage=false", async () => {
943
- mockReadAiConfig.mockResolvedValue(baseConfig);
702
+ test("reports enabled when state exists and messages are not disabled", async () => {
703
+ mockAttemptReadAiState.mockResolvedValue({
704
+ kind: "ok",
705
+ state: baseState,
706
+ });
944
707
 
945
- await statusAiFiles(dummyProjectDir, dummyConvexDir);
708
+ await statusAiFiles({
709
+ projectDir: dummyProjectDir,
710
+ convexDir: dummyConvexDir,
711
+ });
946
712
 
947
713
  expect(mockLogMessage).toHaveBeenCalledWith(
948
714
  expect.stringContaining("enabled"),
@@ -966,18 +732,21 @@ describe("statusAiFiles", () => {
966
732
  "utf8",
967
733
  );
968
734
 
969
- mockReadAiConfig.mockResolvedValue({
970
- ...baseConfig,
971
- guidelinesHash: hash,
735
+ mockAttemptReadAiState.mockResolvedValue({
736
+ kind: "ok",
737
+ state: { ...baseState, guidelinesHash: hash },
972
738
  });
973
739
  mockGetVersion.mockResolvedValue({
974
- message: null,
975
- guidelinesHash: hash,
976
- agentSkillsSha: null,
977
- disableSkillsCli: false,
740
+ kind: "ok",
741
+ data: {
742
+ message: null,
743
+ guidelinesHash: hash,
744
+ agentSkillsSha: null,
745
+ disableSkillsCli: false,
746
+ },
978
747
  });
979
748
 
980
- await statusAiFiles(tmpDir, convexDir);
749
+ await statusAiFiles({ projectDir: tmpDir, convexDir });
981
750
 
982
751
  const calls = mockLogMessage.mock.calls.map((c) => c[0]);
983
752
  expect(calls.some((m) => /guidelines\.md.*up to date/.test(m))).toBe(
@@ -1004,18 +773,21 @@ describe("statusAiFiles", () => {
1004
773
  "utf8",
1005
774
  );
1006
775
 
1007
- mockReadAiConfig.mockResolvedValue({
1008
- ...baseConfig,
1009
- guidelinesHash: hashSha256(content),
776
+ mockAttemptReadAiState.mockResolvedValue({
777
+ kind: "ok",
778
+ state: { ...baseState, guidelinesHash: hashSha256(content) },
1010
779
  });
1011
780
  mockGetVersion.mockResolvedValue({
1012
- message: null,
1013
- guidelinesHash: "new-canonical-hash",
1014
- agentSkillsSha: null,
1015
- disableSkillsCli: false,
781
+ kind: "ok",
782
+ data: {
783
+ message: null,
784
+ guidelinesHash: "new-canonical-hash",
785
+ agentSkillsSha: null,
786
+ disableSkillsCli: false,
787
+ },
1016
788
  });
1017
789
 
1018
- await statusAiFiles(tmpDir, convexDir);
790
+ await statusAiFiles({ projectDir: tmpDir, convexDir });
1019
791
 
1020
792
  expect(mockLogMessage).toHaveBeenCalledWith(
1021
793
  expect.stringContaining("out of date"),
@@ -1043,12 +815,15 @@ describe("statusAiFiles", () => {
1043
815
  "utf8",
1044
816
  );
1045
817
 
1046
- mockReadAiConfig.mockResolvedValue({
1047
- ...baseConfig,
1048
- guidelinesHash: hashSha256("original content"),
818
+ mockAttemptReadAiState.mockResolvedValue({
819
+ kind: "ok",
820
+ state: {
821
+ ...baseState,
822
+ guidelinesHash: hashSha256("original content"),
823
+ },
1049
824
  });
1050
825
 
1051
- await statusAiFiles(tmpDir, convexDir);
826
+ await statusAiFiles({ projectDir: tmpDir, convexDir });
1052
827
 
1053
828
  expect(mockLogMessage).toHaveBeenCalledWith(
1054
829
  expect.stringContaining("modified locally"),
@@ -1058,20 +833,57 @@ describe("statusAiFiles", () => {
1058
833
  }
1059
834
  });
1060
835
 
836
+ test("reports guidelines as missing when guidelines.md is empty", async () => {
837
+ const tmpDir = fs.mkdtempSync(`${os.tmpdir()}${path.sep}`);
838
+ const convexDir = path.join(tmpDir, "convex");
839
+ try {
840
+ fs.mkdirSync(path.join(convexDir, "_generated", "ai"), {
841
+ recursive: true,
842
+ });
843
+ fs.writeFileSync(
844
+ path.join(convexDir, "_generated", "ai", "guidelines.md"),
845
+ "",
846
+ "utf8",
847
+ );
848
+
849
+ mockAttemptReadAiState.mockResolvedValue({
850
+ kind: "ok",
851
+ state: baseState,
852
+ });
853
+
854
+ await statusAiFiles({ projectDir: tmpDir, convexDir });
855
+
856
+ expect(mockLogMessage).toHaveBeenCalledWith(
857
+ expect.stringContaining("guidelines.md: not on disk"),
858
+ );
859
+ } finally {
860
+ fs.rmSync(tmpDir, { recursive: true, force: true });
861
+ }
862
+ });
863
+
1061
864
  test("reports agent skills as out of date when SHA differs from canonical", async () => {
1062
- mockReadAiConfig.mockResolvedValue({
1063
- ...baseConfig,
1064
- installedSkillNames: ["migration-helper"],
1065
- agentSkillsSha: "old-sha",
865
+ mockAttemptReadAiState.mockResolvedValue({
866
+ kind: "ok",
867
+ state: {
868
+ ...baseState,
869
+ installedSkillNames: ["migration-helper"],
870
+ agentSkillsSha: "old-sha",
871
+ },
1066
872
  });
1067
873
  mockGetVersion.mockResolvedValue({
1068
- message: null,
1069
- guidelinesHash: null,
1070
- agentSkillsSha: "new-sha",
1071
- disableSkillsCli: false,
874
+ kind: "ok",
875
+ data: {
876
+ message: null,
877
+ guidelinesHash: null,
878
+ agentSkillsSha: "new-sha",
879
+ disableSkillsCli: false,
880
+ },
1072
881
  });
1073
882
 
1074
- await statusAiFiles(dummyProjectDir, dummyConvexDir);
883
+ await statusAiFiles({
884
+ projectDir: dummyProjectDir,
885
+ convexDir: dummyConvexDir,
886
+ });
1075
887
 
1076
888
  expect(mockLogMessage).toHaveBeenCalledWith(
1077
889
  expect.stringContaining("out of date"),
@@ -1082,28 +894,40 @@ describe("statusAiFiles", () => {
1082
894
  });
1083
895
 
1084
896
  test("skips staleness check when network is unavailable", async () => {
1085
- mockReadAiConfig.mockResolvedValue({
1086
- ...baseConfig,
1087
- guidelinesHash: "old-hash",
1088
- agentSkillsSha: "old-sha",
1089
- installedSkillNames: ["migration-helper"],
897
+ mockAttemptReadAiState.mockResolvedValue({
898
+ kind: "ok",
899
+ state: {
900
+ ...baseState,
901
+ guidelinesHash: "old-hash",
902
+ agentSkillsSha: "old-sha",
903
+ installedSkillNames: ["migration-helper"],
904
+ },
1090
905
  });
1091
- mockGetVersion.mockResolvedValue(null);
906
+ mockGetVersion.mockResolvedValue({ kind: "error" });
1092
907
 
1093
- await statusAiFiles(dummyProjectDir, dummyConvexDir);
908
+ await statusAiFiles({
909
+ projectDir: dummyProjectDir,
910
+ convexDir: dummyConvexDir,
911
+ });
1094
912
 
1095
913
  const calls = mockLogMessage.mock.calls.map((c) => c[0]);
1096
914
  expect(calls.some((m) => /out of date/.test(m))).toBe(false);
1097
915
  });
1098
916
 
1099
917
  test("reports skills with names when installed", async () => {
1100
- mockReadAiConfig.mockResolvedValue({
1101
- ...baseConfig,
1102
- installedSkillNames: ["migration-helper", "schema-builder"],
1103
- agentSkillsSha: "canonical-skills-sha",
918
+ mockAttemptReadAiState.mockResolvedValue({
919
+ kind: "ok",
920
+ state: {
921
+ ...baseState,
922
+ installedSkillNames: ["migration-helper", "schema-builder"],
923
+ agentSkillsSha: "canonical-skills-sha",
924
+ },
1104
925
  });
1105
926
 
1106
- await statusAiFiles(dummyProjectDir, dummyConvexDir);
927
+ await statusAiFiles({
928
+ projectDir: dummyProjectDir,
929
+ convexDir: dummyConvexDir,
930
+ });
1107
931
 
1108
932
  expect(mockLogMessage).toHaveBeenCalledWith(
1109
933
  expect.stringContaining("migration-helper"),
@@ -1122,9 +946,12 @@ describe("statusAiFiles", () => {
1122
946
  "User content\n",
1123
947
  "utf8",
1124
948
  );
1125
- mockReadAiConfig.mockResolvedValue(baseConfig);
949
+ mockAttemptReadAiState.mockResolvedValue({
950
+ kind: "ok",
951
+ state: baseState,
952
+ });
1126
953
 
1127
- await statusAiFiles(tmpDir, convexDir);
954
+ await statusAiFiles({ projectDir: tmpDir, convexDir });
1128
955
 
1129
956
  expect(mockLogMessage).toHaveBeenCalledWith(
1130
957
  expect.stringContaining("CLAUDE.md: no Convex section present"),