convex 1.34.0 → 1.34.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 (243) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/browser.bundle.js +6 -9
  3. package/dist/browser.bundle.js.map +2 -2
  4. package/dist/cjs/browser/sync/authentication_manager.js +4 -1
  5. package/dist/cjs/browser/sync/authentication_manager.js.map +2 -2
  6. package/dist/cjs/browser/sync/web_socket_manager.js +1 -7
  7. package/dist/cjs/browser/sync/web_socket_manager.js.map +2 -2
  8. package/dist/cjs/cli/aiFiles.js +15 -14
  9. package/dist/cjs/cli/aiFiles.js.map +2 -2
  10. package/dist/cjs/cli/configure.js +15 -10
  11. package/dist/cjs/cli/configure.js.map +2 -2
  12. package/dist/cjs/cli/lib/aiFiles/agentsmd.js +69 -0
  13. package/dist/cjs/cli/lib/aiFiles/agentsmd.js.map +7 -0
  14. package/dist/cjs/cli/lib/aiFiles/claudemd.js +69 -0
  15. package/dist/cjs/cli/lib/aiFiles/claudemd.js.map +7 -0
  16. package/dist/cjs/cli/lib/{ai → aiFiles}/config.js +73 -46
  17. package/dist/cjs/cli/lib/aiFiles/config.js.map +7 -0
  18. package/dist/cjs/cli/lib/aiFiles/cursorrules.js +48 -0
  19. package/dist/cjs/cli/lib/aiFiles/cursorrules.js.map +7 -0
  20. package/dist/cjs/cli/lib/aiFiles/guidelinesmd.js +51 -0
  21. package/dist/cjs/cli/lib/aiFiles/guidelinesmd.js.map +7 -0
  22. package/dist/cjs/cli/lib/aiFiles/index.js +231 -0
  23. package/dist/cjs/cli/lib/aiFiles/index.js.map +7 -0
  24. package/dist/cjs/cli/lib/aiFiles/paths.js.map +7 -0
  25. package/dist/cjs/cli/lib/aiFiles/skills.js +180 -0
  26. package/dist/cjs/cli/lib/aiFiles/skills.js.map +7 -0
  27. package/dist/cjs/cli/lib/aiFiles/status.js +195 -0
  28. package/dist/cjs/cli/lib/aiFiles/status.js.map +7 -0
  29. package/dist/cjs/cli/lib/aiFiles/utils.js +111 -0
  30. package/dist/cjs/cli/lib/aiFiles/utils.js.map +7 -0
  31. package/dist/cjs/cli/lib/command.js +6 -1
  32. package/dist/cjs/cli/lib/command.js.map +2 -2
  33. package/dist/cjs/cli/lib/config.js +3 -4
  34. package/dist/cjs/cli/lib/config.js.map +2 -2
  35. package/dist/cjs/cli/lib/localDeployment/anonymous.js +2 -2
  36. package/dist/cjs/cli/lib/localDeployment/anonymous.js.map +2 -2
  37. package/dist/cjs/cli/lib/updates.js +8 -8
  38. package/dist/cjs/cli/lib/updates.js.map +2 -2
  39. package/dist/cjs/cli/lib/versionApi.js +7 -4
  40. package/dist/cjs/cli/lib/versionApi.js.map +2 -2
  41. package/dist/cjs/cli/lib/workos/workos.js +4 -6
  42. package/dist/cjs/cli/lib/workos/workos.js.map +2 -2
  43. package/dist/cjs/index.js +1 -1
  44. package/dist/cjs/index.js.map +1 -1
  45. package/dist/cjs-types/browser/sync/authentication_manager.d.ts.map +1 -1
  46. package/dist/cjs-types/browser/sync/web_socket_manager.d.ts.map +1 -1
  47. package/dist/cjs-types/cli/aiFiles.d.ts.map +1 -1
  48. package/dist/cjs-types/cli/configure.d.ts.map +1 -1
  49. package/dist/cjs-types/cli/lib/aiFiles/agentsmd.d.ts +19 -0
  50. package/dist/cjs-types/cli/lib/aiFiles/agentsmd.d.ts.map +1 -0
  51. package/dist/cjs-types/cli/lib/aiFiles/agentsmd.test.d.ts +2 -0
  52. package/dist/cjs-types/cli/lib/aiFiles/agentsmd.test.d.ts.map +1 -0
  53. package/dist/cjs-types/cli/lib/aiFiles/claudemd.d.ts +19 -0
  54. package/dist/cjs-types/cli/lib/aiFiles/claudemd.d.ts.map +1 -0
  55. package/dist/cjs-types/cli/lib/aiFiles/claudemd.test.d.ts +2 -0
  56. package/dist/cjs-types/cli/lib/aiFiles/claudemd.test.d.ts.map +1 -0
  57. package/dist/cjs-types/cli/lib/aiFiles/config.d.ts +46 -0
  58. package/dist/cjs-types/cli/lib/aiFiles/config.d.ts.map +1 -0
  59. package/dist/cjs-types/cli/lib/aiFiles/config.test.d.ts.map +1 -0
  60. package/dist/cjs-types/cli/lib/aiFiles/cursorrules.d.ts +10 -0
  61. package/dist/cjs-types/cli/lib/aiFiles/cursorrules.d.ts.map +1 -0
  62. package/dist/cjs-types/cli/lib/aiFiles/guidelinesmd.d.ts +12 -0
  63. package/dist/cjs-types/cli/lib/aiFiles/guidelinesmd.d.ts.map +1 -0
  64. package/dist/cjs-types/cli/lib/aiFiles/guidelinesmd.test.d.ts +2 -0
  65. package/dist/cjs-types/cli/lib/aiFiles/guidelinesmd.test.d.ts.map +1 -0
  66. package/dist/cjs-types/cli/lib/aiFiles/index.d.ts +40 -0
  67. package/dist/cjs-types/cli/lib/aiFiles/index.d.ts.map +1 -0
  68. package/dist/cjs-types/cli/lib/aiFiles/index.test.d.ts.map +1 -0
  69. package/dist/cjs-types/cli/lib/aiFiles/integration.test.d.ts.map +1 -0
  70. package/dist/cjs-types/cli/lib/{ai → aiFiles}/paths.d.ts +4 -0
  71. package/dist/cjs-types/cli/lib/aiFiles/paths.d.ts.map +1 -0
  72. package/dist/cjs-types/cli/lib/aiFiles/prompt.test.d.ts.map +1 -0
  73. package/dist/cjs-types/cli/lib/aiFiles/skills.d.ts +18 -0
  74. package/dist/cjs-types/cli/lib/aiFiles/skills.d.ts.map +1 -0
  75. package/dist/cjs-types/cli/lib/aiFiles/status.d.ts +3 -0
  76. package/dist/cjs-types/cli/lib/aiFiles/status.d.ts.map +1 -0
  77. package/dist/cjs-types/cli/lib/aiFiles/utils.d.ts +46 -0
  78. package/dist/cjs-types/cli/lib/aiFiles/utils.d.ts.map +1 -0
  79. package/dist/cjs-types/cli/lib/config.d.ts +1 -0
  80. package/dist/cjs-types/cli/lib/config.d.ts.map +1 -1
  81. package/dist/cjs-types/cli/lib/versionApi.d.ts +7 -1
  82. package/dist/cjs-types/cli/lib/versionApi.d.ts.map +1 -1
  83. package/dist/cjs-types/cli/lib/workos/workos.d.ts.map +1 -1
  84. package/dist/cjs-types/index.d.ts +1 -1
  85. package/dist/cli.bundle.cjs +1605 -1548
  86. package/dist/cli.bundle.cjs.map +4 -4
  87. package/dist/esm/browser/sync/authentication_manager.js +4 -1
  88. package/dist/esm/browser/sync/authentication_manager.js.map +2 -2
  89. package/dist/esm/browser/sync/web_socket_manager.js +1 -7
  90. package/dist/esm/browser/sync/web_socket_manager.js.map +2 -2
  91. package/dist/esm/cli/aiFiles.js +17 -17
  92. package/dist/esm/cli/aiFiles.js.map +2 -2
  93. package/dist/esm/cli/configure.js +15 -10
  94. package/dist/esm/cli/configure.js.map +2 -2
  95. package/dist/esm/cli/lib/aiFiles/agentsmd.js +52 -0
  96. package/dist/esm/cli/lib/aiFiles/agentsmd.js.map +7 -0
  97. package/dist/esm/cli/lib/aiFiles/claudemd.js +52 -0
  98. package/dist/esm/cli/lib/aiFiles/claudemd.js.map +7 -0
  99. package/dist/esm/cli/lib/{ai → aiFiles}/config.js +71 -45
  100. package/dist/esm/cli/lib/aiFiles/config.js.map +7 -0
  101. package/dist/esm/cli/lib/aiFiles/cursorrules.js +16 -0
  102. package/dist/esm/cli/lib/aiFiles/cursorrules.js.map +7 -0
  103. package/dist/esm/cli/lib/aiFiles/guidelinesmd.js +28 -0
  104. package/dist/esm/cli/lib/aiFiles/guidelinesmd.js.map +7 -0
  105. package/dist/esm/cli/lib/aiFiles/index.js +210 -0
  106. package/dist/esm/cli/lib/aiFiles/index.js.map +7 -0
  107. package/dist/esm/cli/lib/aiFiles/paths.js.map +7 -0
  108. package/dist/esm/cli/lib/aiFiles/skills.js +147 -0
  109. package/dist/esm/cli/lib/aiFiles/skills.js.map +7 -0
  110. package/dist/esm/cli/lib/aiFiles/status.js +175 -0
  111. package/dist/esm/cli/lib/aiFiles/status.js.map +7 -0
  112. package/dist/esm/cli/lib/aiFiles/utils.js +82 -0
  113. package/dist/esm/cli/lib/aiFiles/utils.js.map +7 -0
  114. package/dist/esm/cli/lib/command.js +6 -1
  115. package/dist/esm/cli/lib/command.js.map +2 -2
  116. package/dist/esm/cli/lib/config.js +3 -4
  117. package/dist/esm/cli/lib/config.js.map +2 -2
  118. package/dist/esm/cli/lib/localDeployment/anonymous.js +2 -2
  119. package/dist/esm/cli/lib/localDeployment/anonymous.js.map +2 -2
  120. package/dist/esm/cli/lib/updates.js +8 -8
  121. package/dist/esm/cli/lib/updates.js.map +2 -2
  122. package/dist/esm/cli/lib/versionApi.js +7 -4
  123. package/dist/esm/cli/lib/versionApi.js.map +2 -2
  124. package/dist/esm/cli/lib/workos/workos.js +4 -6
  125. package/dist/esm/cli/lib/workos/workos.js.map +2 -2
  126. package/dist/esm/index.js +1 -1
  127. package/dist/esm/index.js.map +1 -1
  128. package/dist/esm-types/browser/sync/authentication_manager.d.ts.map +1 -1
  129. package/dist/esm-types/browser/sync/web_socket_manager.d.ts.map +1 -1
  130. package/dist/esm-types/cli/aiFiles.d.ts.map +1 -1
  131. package/dist/esm-types/cli/configure.d.ts.map +1 -1
  132. package/dist/esm-types/cli/lib/aiFiles/agentsmd.d.ts +19 -0
  133. package/dist/esm-types/cli/lib/aiFiles/agentsmd.d.ts.map +1 -0
  134. package/dist/esm-types/cli/lib/aiFiles/agentsmd.test.d.ts +2 -0
  135. package/dist/esm-types/cli/lib/aiFiles/agentsmd.test.d.ts.map +1 -0
  136. package/dist/esm-types/cli/lib/aiFiles/claudemd.d.ts +19 -0
  137. package/dist/esm-types/cli/lib/aiFiles/claudemd.d.ts.map +1 -0
  138. package/dist/esm-types/cli/lib/aiFiles/claudemd.test.d.ts +2 -0
  139. package/dist/esm-types/cli/lib/aiFiles/claudemd.test.d.ts.map +1 -0
  140. package/dist/esm-types/cli/lib/aiFiles/config.d.ts +46 -0
  141. package/dist/esm-types/cli/lib/aiFiles/config.d.ts.map +1 -0
  142. package/dist/esm-types/cli/lib/aiFiles/config.test.d.ts.map +1 -0
  143. package/dist/esm-types/cli/lib/aiFiles/cursorrules.d.ts +10 -0
  144. package/dist/esm-types/cli/lib/aiFiles/cursorrules.d.ts.map +1 -0
  145. package/dist/esm-types/cli/lib/aiFiles/guidelinesmd.d.ts +12 -0
  146. package/dist/esm-types/cli/lib/aiFiles/guidelinesmd.d.ts.map +1 -0
  147. package/dist/esm-types/cli/lib/aiFiles/guidelinesmd.test.d.ts +2 -0
  148. package/dist/esm-types/cli/lib/aiFiles/guidelinesmd.test.d.ts.map +1 -0
  149. package/dist/esm-types/cli/lib/aiFiles/index.d.ts +40 -0
  150. package/dist/esm-types/cli/lib/aiFiles/index.d.ts.map +1 -0
  151. package/dist/esm-types/cli/lib/aiFiles/index.test.d.ts.map +1 -0
  152. package/dist/esm-types/cli/lib/aiFiles/integration.test.d.ts.map +1 -0
  153. package/dist/esm-types/cli/lib/{ai → aiFiles}/paths.d.ts +4 -0
  154. package/dist/esm-types/cli/lib/aiFiles/paths.d.ts.map +1 -0
  155. package/dist/esm-types/cli/lib/aiFiles/prompt.test.d.ts.map +1 -0
  156. package/dist/esm-types/cli/lib/aiFiles/skills.d.ts +18 -0
  157. package/dist/esm-types/cli/lib/aiFiles/skills.d.ts.map +1 -0
  158. package/dist/esm-types/cli/lib/aiFiles/status.d.ts +3 -0
  159. package/dist/esm-types/cli/lib/aiFiles/status.d.ts.map +1 -0
  160. package/dist/esm-types/cli/lib/aiFiles/utils.d.ts +46 -0
  161. package/dist/esm-types/cli/lib/aiFiles/utils.d.ts.map +1 -0
  162. package/dist/esm-types/cli/lib/config.d.ts +1 -0
  163. package/dist/esm-types/cli/lib/config.d.ts.map +1 -1
  164. package/dist/esm-types/cli/lib/versionApi.d.ts +7 -1
  165. package/dist/esm-types/cli/lib/versionApi.d.ts.map +1 -1
  166. package/dist/esm-types/cli/lib/workos/workos.d.ts.map +1 -1
  167. package/dist/esm-types/index.d.ts +1 -1
  168. package/dist/react.bundle.js +6 -9
  169. package/dist/react.bundle.js.map +2 -2
  170. package/package.json +1 -1
  171. package/schemas/convex.schema.json +7 -1
  172. package/src/browser/sync/authentication_manager.ts +9 -4
  173. package/src/browser/sync/client_node.test.ts +125 -0
  174. package/src/browser/sync/web_socket_manager.ts +1 -7
  175. package/src/cli/aiFiles.ts +20 -27
  176. package/src/cli/configure.ts +17 -11
  177. package/src/cli/deploymentSelection.test.ts +56 -2
  178. package/src/cli/lib/{ai → aiFiles}/MANUAL_TESTING.md +6 -2
  179. package/src/cli/lib/aiFiles/agentsmd.test.ts +133 -0
  180. package/src/cli/lib/aiFiles/agentsmd.ts +77 -0
  181. package/src/cli/lib/aiFiles/claudemd.test.ts +92 -0
  182. package/src/cli/lib/aiFiles/claudemd.ts +77 -0
  183. package/src/cli/lib/{ai → aiFiles}/config.test.ts +181 -59
  184. package/src/cli/lib/{ai → aiFiles}/config.ts +92 -63
  185. package/src/cli/lib/aiFiles/cursorrules.ts +25 -0
  186. package/src/cli/lib/aiFiles/guidelinesmd.test.ts +40 -0
  187. package/src/cli/lib/aiFiles/guidelinesmd.ts +41 -0
  188. package/src/cli/lib/{ai → aiFiles}/index.test.ts +200 -339
  189. package/src/cli/lib/aiFiles/index.ts +303 -0
  190. package/src/cli/lib/{ai → aiFiles}/integration.test.ts +117 -147
  191. package/src/cli/lib/{ai → aiFiles}/paths.ts +5 -0
  192. package/src/cli/lib/{ai → aiFiles}/prompt.test.ts +78 -30
  193. package/src/cli/lib/aiFiles/skills.ts +213 -0
  194. package/src/cli/lib/aiFiles/status.ts +240 -0
  195. package/src/cli/lib/aiFiles/utils.ts +163 -0
  196. package/src/cli/lib/command.ts +6 -1
  197. package/src/cli/lib/config.test.ts +1 -1
  198. package/src/cli/lib/config.ts +6 -5
  199. package/src/cli/lib/localDeployment/anonymous.ts +2 -2
  200. package/src/cli/lib/updates.test.ts +40 -30
  201. package/src/cli/lib/updates.ts +8 -8
  202. package/src/cli/lib/versionApi.test.ts +13 -10
  203. package/src/cli/lib/versionApi.ts +13 -5
  204. package/src/cli/lib/workos/workos.ts +4 -5
  205. package/src/index.ts +1 -1
  206. package/src/values/.claude/settings.local.json +10 -0
  207. package/dist/cjs/cli/lib/ai/config.js.map +0 -7
  208. package/dist/cjs/cli/lib/ai/index.js +0 -704
  209. package/dist/cjs/cli/lib/ai/index.js.map +0 -7
  210. package/dist/cjs/cli/lib/ai/paths.js.map +0 -7
  211. package/dist/cjs-types/cli/lib/ai/config.d.ts +0 -50
  212. package/dist/cjs-types/cli/lib/ai/config.d.ts.map +0 -1
  213. package/dist/cjs-types/cli/lib/ai/config.test.d.ts.map +0 -1
  214. package/dist/cjs-types/cli/lib/ai/index.d.ts +0 -56
  215. package/dist/cjs-types/cli/lib/ai/index.d.ts.map +0 -1
  216. package/dist/cjs-types/cli/lib/ai/index.test.d.ts.map +0 -1
  217. package/dist/cjs-types/cli/lib/ai/integration.test.d.ts.map +0 -1
  218. package/dist/cjs-types/cli/lib/ai/paths.d.ts.map +0 -1
  219. package/dist/cjs-types/cli/lib/ai/prompt.test.d.ts.map +0 -1
  220. package/dist/esm/cli/lib/ai/config.js.map +0 -7
  221. package/dist/esm/cli/lib/ai/index.js +0 -684
  222. package/dist/esm/cli/lib/ai/index.js.map +0 -7
  223. package/dist/esm/cli/lib/ai/paths.js.map +0 -7
  224. package/dist/esm-types/cli/lib/ai/config.d.ts +0 -50
  225. package/dist/esm-types/cli/lib/ai/config.d.ts.map +0 -1
  226. package/dist/esm-types/cli/lib/ai/config.test.d.ts.map +0 -1
  227. package/dist/esm-types/cli/lib/ai/index.d.ts +0 -56
  228. package/dist/esm-types/cli/lib/ai/index.d.ts.map +0 -1
  229. package/dist/esm-types/cli/lib/ai/index.test.d.ts.map +0 -1
  230. package/dist/esm-types/cli/lib/ai/integration.test.d.ts.map +0 -1
  231. package/dist/esm-types/cli/lib/ai/paths.d.ts.map +0 -1
  232. package/dist/esm-types/cli/lib/ai/prompt.test.d.ts.map +0 -1
  233. package/src/cli/lib/ai/index.ts +0 -1006
  234. /package/dist/cjs/cli/lib/{ai → aiFiles}/paths.js +0 -0
  235. /package/dist/cjs-types/cli/lib/{ai → aiFiles}/config.test.d.ts +0 -0
  236. /package/dist/cjs-types/cli/lib/{ai → aiFiles}/index.test.d.ts +0 -0
  237. /package/dist/cjs-types/cli/lib/{ai → aiFiles}/integration.test.d.ts +0 -0
  238. /package/dist/cjs-types/cli/lib/{ai → aiFiles}/prompt.test.d.ts +0 -0
  239. /package/dist/esm/cli/lib/{ai → aiFiles}/paths.js +0 -0
  240. /package/dist/esm-types/cli/lib/{ai → aiFiles}/config.test.d.ts +0 -0
  241. /package/dist/esm-types/cli/lib/{ai → aiFiles}/index.test.d.ts +0 -0
  242. /package/dist/esm-types/cli/lib/{ai → aiFiles}/integration.test.d.ts +0 -0
  243. /package/dist/esm-types/cli/lib/{ai → aiFiles}/prompt.test.d.ts +0 -0
@@ -0,0 +1,303 @@
1
+ import * as Sentry from "@sentry/node";
2
+ import path from "path";
3
+ import { Context } from "../../../bundler/context.js";
4
+ // eslint-disable-next-line no-restricted-imports
5
+ import { promises as fs } from "fs";
6
+ import { chalkStderr } from "chalk";
7
+ import { logMessage } from "../../../bundler/log.js";
8
+ import { promptYesNo } from "../utils/prompts.js";
9
+ import { type AiFilesPaths, aiDirForConvexDir } from "./paths.js";
10
+ import {
11
+ installGuidelinesFile,
12
+ hasGuidelinesInstalled,
13
+ } from "./guidelinesmd.js";
14
+ import {
15
+ type AiFilesConfig,
16
+ hasAiFilesConfig,
17
+ readAiConfig,
18
+ writeAiConfig,
19
+ writeAiEnabledToProjectConfig,
20
+ } from "./config.js";
21
+ import { isInInteractiveTerminal } from "./utils.js";
22
+ import {
23
+ hasAgentsMdInstalled,
24
+ applyAgentsMdSection,
25
+ removeAgentsMdSection,
26
+ } from "./agentsmd.js";
27
+ import {
28
+ hasClaudeMdInstalled,
29
+ applyClaudeMdSection,
30
+ removeClaudeMdSection,
31
+ } from "./claudemd.js";
32
+ import { installSkills, removeInstalledSkills } from "./skills.js";
33
+ import { removeLegacyCursorRulesFile as removeLegacyCursorRules } from "./cursorrules.js";
34
+ async function hasExistingAiFilesArtifacts({
35
+ projectDir,
36
+ convexDir,
37
+ }: AiFilesPaths): Promise<boolean> {
38
+ return (
39
+ (await hasGuidelinesInstalled(convexDir)) ||
40
+ (await hasAgentsMdInstalled(projectDir)) ||
41
+ (await hasClaudeMdInstalled(projectDir))
42
+ );
43
+ }
44
+
45
+ /**
46
+ * Install or refresh all Convex AI files.
47
+ *
48
+ * Reads the existing config if present, or starts from a blank one for a
49
+ * fresh install. Each component can be individually skipped via the optional
50
+ * flags (all default to true).
51
+ */
52
+ export async function installAiFiles({
53
+ projectDir,
54
+ convexDir,
55
+ shouldWriteGuidelines = true,
56
+ shouldWriteAgentsMd = true,
57
+ shouldWriteClaudeMd = true,
58
+ shouldWriteSkills = true,
59
+ }: AiFilesPaths & {
60
+ shouldWriteGuidelines?: boolean;
61
+ shouldWriteAgentsMd?: boolean;
62
+ shouldWriteClaudeMd?: boolean;
63
+ shouldWriteSkills?: boolean;
64
+ }): Promise<void> {
65
+ await fs.mkdir(aiDirForConvexDir(convexDir), { recursive: true });
66
+
67
+ const config: AiFilesConfig = (await readAiConfig({
68
+ projectDir,
69
+ convexDir,
70
+ })) ?? {
71
+ enabled: true,
72
+ guidelinesHash: null,
73
+ agentsMdSectionHash: null,
74
+ claudeMdHash: null,
75
+ agentSkillsSha: null,
76
+ installedSkillNames: [],
77
+ };
78
+
79
+ if (shouldWriteGuidelines) await installGuidelinesFile({ convexDir, config });
80
+
81
+ const convexDirName = path.relative(projectDir, convexDir);
82
+
83
+ if (shouldWriteAgentsMd)
84
+ await applyAgentsMdSection({ projectDir, config, convexDirName });
85
+
86
+ if (shouldWriteClaudeMd)
87
+ await applyClaudeMdSection({ projectDir, config, convexDirName });
88
+
89
+ if (shouldWriteSkills) await installSkills({ projectDir, config });
90
+
91
+ await removeLegacyCursorRules(projectDir);
92
+ await writeAiConfig({ config, projectDir, convexDir });
93
+
94
+ logMessage(`${chalkStderr.green("✔")} Convex AI files installed.`);
95
+ }
96
+
97
+ async function attemptToInstallAiFiles(
98
+ opts: Parameters<typeof installAiFiles>[0],
99
+ ): Promise<void> {
100
+ try {
101
+ await installAiFiles(opts);
102
+ } catch (error) {
103
+ Sentry.captureException(error);
104
+ }
105
+ }
106
+
107
+ type AiFilesStalenessStatus =
108
+ | "not-installed" // no config AND no artifacts — show install nag
109
+ | "has-artifacts" // no config but files exist on disk (e.g. fresh checkout) — stay quiet
110
+ | "disabled" // user opted out of nag messages
111
+ | "stale" // one or more files are out of date
112
+ | "up-to-date"; // everything looks fine
113
+
114
+ async function determineAiFilesStaleness({
115
+ canonicalGuidelinesHash,
116
+ canonicalAgentSkillsSha,
117
+ projectDir,
118
+ convexDir,
119
+ }: {
120
+ canonicalGuidelinesHash: string | null;
121
+ canonicalAgentSkillsSha: string | null;
122
+ } & AiFilesPaths): Promise<AiFilesStalenessStatus> {
123
+ const config = await readAiConfig({ projectDir, convexDir });
124
+
125
+ if (config === null) {
126
+ const hasArtifacts = await hasExistingAiFilesArtifacts({
127
+ projectDir,
128
+ convexDir,
129
+ });
130
+ return hasArtifacts ? "has-artifacts" : "not-installed";
131
+ }
132
+
133
+ if (!config.enabled) return "disabled";
134
+
135
+ if (canonicalGuidelinesHash === null && canonicalAgentSkillsSha === null)
136
+ return "up-to-date";
137
+
138
+ const guidelinesStale =
139
+ canonicalGuidelinesHash !== null &&
140
+ config.guidelinesHash !== null &&
141
+ config.guidelinesHash !== canonicalGuidelinesHash;
142
+
143
+ const skillsStale =
144
+ canonicalAgentSkillsSha !== null &&
145
+ config.agentSkillsSha !== null &&
146
+ config.agentSkillsSha !== canonicalAgentSkillsSha;
147
+
148
+ return guidelinesStale || skillsStale ? "stale" : "up-to-date";
149
+ }
150
+
151
+ /**
152
+ * Check whether the Convex AI files are out of date and log a nag message
153
+ * if so.
154
+ */
155
+ export async function checkAiFilesStaleness(
156
+ opts: {
157
+ canonicalGuidelinesHash: string | null;
158
+ canonicalAgentSkillsSha: string | null;
159
+ } & AiFilesPaths,
160
+ ): Promise<void> {
161
+ const status = await determineAiFilesStaleness(opts);
162
+
163
+ if (status === "not-installed") {
164
+ logMessage(
165
+ chalkStderr.yellow(
166
+ `Convex AI files are not installed. Run ${chalkStderr.bold(`npx convex ai-files install`)} to get started or ${chalkStderr.bold(`npx convex ai-files disable`)} to hide this message.`,
167
+ ),
168
+ );
169
+ }
170
+
171
+ if (status === "stale") {
172
+ logMessage(
173
+ chalkStderr.yellow(
174
+ `Your Convex AI files are out of date. Run ${chalkStderr.bold(`npx convex ai-files update`)} to get the latest.`,
175
+ ),
176
+ );
177
+ }
178
+ }
179
+
180
+ export async function enableAiFiles({
181
+ projectDir,
182
+ convexDir,
183
+ }: AiFilesPaths): Promise<void> {
184
+ await installAiFiles({ projectDir, convexDir });
185
+ const config = await readAiConfig({ projectDir, convexDir });
186
+ if (config === null) return;
187
+ config.enabled = true;
188
+ await writeAiConfig({
189
+ config,
190
+ projectDir,
191
+ convexDir,
192
+ options: { persistEnabledPreference: "always" },
193
+ });
194
+ }
195
+
196
+ /**
197
+ * Remove all Convex AI files from the project.
198
+ * Called by `npx convex ai-files remove`.
199
+ */
200
+ export async function removeAiFiles({
201
+ projectDir,
202
+ convexDir,
203
+ }: AiFilesPaths): Promise<void> {
204
+ const config = await readAiConfig({ projectDir, convexDir });
205
+ if (config === null) {
206
+ logMessage("No Convex AI files found — nothing to remove.");
207
+ return;
208
+ }
209
+
210
+ const removals = [
211
+ await removeAgentsMdSection(projectDir),
212
+ await removeClaudeMdSection(projectDir),
213
+ await removeInstalledSkills({
214
+ projectDir,
215
+ skillNames: config.installedSkillNames,
216
+ }),
217
+ await removeLegacyCursorRules(projectDir),
218
+ await attemptToDeleteAiDir({ projectDir, convexDir }),
219
+ ];
220
+
221
+ if (removals.some(Boolean)) logMessage("Convex AI files removed.");
222
+ }
223
+
224
+ /**
225
+ * Called by `npx convex ai-files disable`.
226
+ *
227
+ * Writes a suppression flag into `convex.json` so `npx convex dev` stops
228
+ * showing AI files install/staleness messages. Files are left in place.
229
+ */
230
+ export async function safelyAttemptToDisableAiFiles(
231
+ projectDir: string,
232
+ ): Promise<void> {
233
+ try {
234
+ await writeAiEnabledToProjectConfig({
235
+ projectDir,
236
+ enabled: false,
237
+ });
238
+ logMessage(
239
+ `${chalkStderr.green(`✔`)} Convex AI files disabled. Run ${chalkStderr.bold(`npx convex ai-files enable`)} to re-enable.`,
240
+ );
241
+ } catch (error) {
242
+ Sentry.captureException(error);
243
+ logMessage(
244
+ chalkStderr.yellow(
245
+ "Could not write AI message suppression config. Message may reappear.",
246
+ ),
247
+ );
248
+ }
249
+ }
250
+
251
+ async function attemptToDeleteAiDir({
252
+ projectDir,
253
+ convexDir,
254
+ }: AiFilesPaths): Promise<boolean> {
255
+ const aiDir = aiDirForConvexDir(convexDir);
256
+ const relPath = path.relative(projectDir, aiDir);
257
+ try {
258
+ await fs.rm(aiDir, { recursive: true, force: true });
259
+ logMessage(`${chalkStderr.green("✔")} Deleted ${relPath}/`);
260
+ return true;
261
+ } catch (error) {
262
+ Sentry.captureException(error);
263
+ logMessage(
264
+ chalkStderr.yellow(`Could not delete ${relPath}/. Remove it manually.`),
265
+ );
266
+ return false;
267
+ }
268
+ }
269
+
270
+ async function hasAiFilesBeenInstalledBefore({
271
+ projectDir,
272
+ convexDir,
273
+ }: AiFilesPaths): Promise<boolean> {
274
+ return (
275
+ (await hasAiFilesConfig({ projectDir, convexDir })) ||
276
+ (await hasExistingAiFilesArtifacts({ projectDir, convexDir }))
277
+ );
278
+ }
279
+
280
+ export async function maybeSetupAiFiles({
281
+ ctx,
282
+ convexDir,
283
+ projectDir,
284
+ }: {
285
+ ctx: Context;
286
+ } & AiFilesPaths): Promise<void> {
287
+ if (!isInInteractiveTerminal()) return;
288
+
289
+ const config = await readAiConfig({ projectDir, convexDir });
290
+ if (config !== null && !config.enabled) return;
291
+
292
+ if (await hasAiFilesBeenInstalledBefore({ projectDir, convexDir })) {
293
+ await attemptToInstallAiFiles({ projectDir, convexDir });
294
+ return;
295
+ }
296
+
297
+ const shouldInstall = await promptYesNo(ctx, {
298
+ message: "Set up Convex AI files? (guidelines, AGENTS.md, agent skills)",
299
+ default: true,
300
+ });
301
+
302
+ if (shouldInstall) await attemptToInstallAiFiles({ projectDir, convexDir });
303
+ }