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.
- package/CHANGELOG.md +12 -0
- package/dist/browser.bundle.js +6 -9
- package/dist/browser.bundle.js.map +2 -2
- package/dist/cjs/browser/sync/authentication_manager.js +4 -1
- package/dist/cjs/browser/sync/authentication_manager.js.map +2 -2
- package/dist/cjs/browser/sync/web_socket_manager.js +1 -7
- package/dist/cjs/browser/sync/web_socket_manager.js.map +2 -2
- package/dist/cjs/cli/aiFiles.js +15 -14
- package/dist/cjs/cli/aiFiles.js.map +2 -2
- package/dist/cjs/cli/configure.js +15 -10
- package/dist/cjs/cli/configure.js.map +2 -2
- package/dist/cjs/cli/lib/aiFiles/agentsmd.js +69 -0
- package/dist/cjs/cli/lib/aiFiles/agentsmd.js.map +7 -0
- package/dist/cjs/cli/lib/aiFiles/claudemd.js +69 -0
- package/dist/cjs/cli/lib/aiFiles/claudemd.js.map +7 -0
- package/dist/cjs/cli/lib/{ai → aiFiles}/config.js +73 -46
- package/dist/cjs/cli/lib/aiFiles/config.js.map +7 -0
- package/dist/cjs/cli/lib/aiFiles/cursorrules.js +48 -0
- package/dist/cjs/cli/lib/aiFiles/cursorrules.js.map +7 -0
- package/dist/cjs/cli/lib/aiFiles/guidelinesmd.js +51 -0
- package/dist/cjs/cli/lib/aiFiles/guidelinesmd.js.map +7 -0
- package/dist/cjs/cli/lib/aiFiles/index.js +231 -0
- package/dist/cjs/cli/lib/aiFiles/index.js.map +7 -0
- package/dist/cjs/cli/lib/aiFiles/paths.js.map +7 -0
- package/dist/cjs/cli/lib/aiFiles/skills.js +180 -0
- package/dist/cjs/cli/lib/aiFiles/skills.js.map +7 -0
- package/dist/cjs/cli/lib/aiFiles/status.js +195 -0
- package/dist/cjs/cli/lib/aiFiles/status.js.map +7 -0
- package/dist/cjs/cli/lib/aiFiles/utils.js +111 -0
- package/dist/cjs/cli/lib/aiFiles/utils.js.map +7 -0
- package/dist/cjs/cli/lib/command.js +6 -1
- package/dist/cjs/cli/lib/command.js.map +2 -2
- package/dist/cjs/cli/lib/config.js +3 -4
- package/dist/cjs/cli/lib/config.js.map +2 -2
- package/dist/cjs/cli/lib/localDeployment/anonymous.js +2 -2
- package/dist/cjs/cli/lib/localDeployment/anonymous.js.map +2 -2
- package/dist/cjs/cli/lib/updates.js +8 -8
- package/dist/cjs/cli/lib/updates.js.map +2 -2
- package/dist/cjs/cli/lib/versionApi.js +7 -4
- package/dist/cjs/cli/lib/versionApi.js.map +2 -2
- package/dist/cjs/cli/lib/workos/workos.js +4 -6
- package/dist/cjs/cli/lib/workos/workos.js.map +2 -2
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs-types/browser/sync/authentication_manager.d.ts.map +1 -1
- package/dist/cjs-types/browser/sync/web_socket_manager.d.ts.map +1 -1
- package/dist/cjs-types/cli/aiFiles.d.ts.map +1 -1
- package/dist/cjs-types/cli/configure.d.ts.map +1 -1
- package/dist/cjs-types/cli/lib/aiFiles/agentsmd.d.ts +19 -0
- package/dist/cjs-types/cli/lib/aiFiles/agentsmd.d.ts.map +1 -0
- package/dist/cjs-types/cli/lib/aiFiles/agentsmd.test.d.ts +2 -0
- package/dist/cjs-types/cli/lib/aiFiles/agentsmd.test.d.ts.map +1 -0
- package/dist/cjs-types/cli/lib/aiFiles/claudemd.d.ts +19 -0
- package/dist/cjs-types/cli/lib/aiFiles/claudemd.d.ts.map +1 -0
- package/dist/cjs-types/cli/lib/aiFiles/claudemd.test.d.ts +2 -0
- package/dist/cjs-types/cli/lib/aiFiles/claudemd.test.d.ts.map +1 -0
- package/dist/cjs-types/cli/lib/aiFiles/config.d.ts +46 -0
- package/dist/cjs-types/cli/lib/aiFiles/config.d.ts.map +1 -0
- package/dist/cjs-types/cli/lib/aiFiles/config.test.d.ts.map +1 -0
- package/dist/cjs-types/cli/lib/aiFiles/cursorrules.d.ts +10 -0
- package/dist/cjs-types/cli/lib/aiFiles/cursorrules.d.ts.map +1 -0
- package/dist/cjs-types/cli/lib/aiFiles/guidelinesmd.d.ts +12 -0
- package/dist/cjs-types/cli/lib/aiFiles/guidelinesmd.d.ts.map +1 -0
- package/dist/cjs-types/cli/lib/aiFiles/guidelinesmd.test.d.ts +2 -0
- package/dist/cjs-types/cli/lib/aiFiles/guidelinesmd.test.d.ts.map +1 -0
- package/dist/cjs-types/cli/lib/aiFiles/index.d.ts +40 -0
- package/dist/cjs-types/cli/lib/aiFiles/index.d.ts.map +1 -0
- package/dist/cjs-types/cli/lib/aiFiles/index.test.d.ts.map +1 -0
- package/dist/cjs-types/cli/lib/aiFiles/integration.test.d.ts.map +1 -0
- package/dist/cjs-types/cli/lib/{ai → aiFiles}/paths.d.ts +4 -0
- package/dist/cjs-types/cli/lib/aiFiles/paths.d.ts.map +1 -0
- package/dist/cjs-types/cli/lib/aiFiles/prompt.test.d.ts.map +1 -0
- package/dist/cjs-types/cli/lib/aiFiles/skills.d.ts +18 -0
- package/dist/cjs-types/cli/lib/aiFiles/skills.d.ts.map +1 -0
- package/dist/cjs-types/cli/lib/aiFiles/status.d.ts +3 -0
- package/dist/cjs-types/cli/lib/aiFiles/status.d.ts.map +1 -0
- package/dist/cjs-types/cli/lib/aiFiles/utils.d.ts +46 -0
- package/dist/cjs-types/cli/lib/aiFiles/utils.d.ts.map +1 -0
- package/dist/cjs-types/cli/lib/config.d.ts +1 -0
- package/dist/cjs-types/cli/lib/config.d.ts.map +1 -1
- package/dist/cjs-types/cli/lib/versionApi.d.ts +7 -1
- package/dist/cjs-types/cli/lib/versionApi.d.ts.map +1 -1
- package/dist/cjs-types/cli/lib/workos/workos.d.ts.map +1 -1
- package/dist/cjs-types/index.d.ts +1 -1
- package/dist/cli.bundle.cjs +1605 -1548
- package/dist/cli.bundle.cjs.map +4 -4
- package/dist/esm/browser/sync/authentication_manager.js +4 -1
- package/dist/esm/browser/sync/authentication_manager.js.map +2 -2
- package/dist/esm/browser/sync/web_socket_manager.js +1 -7
- package/dist/esm/browser/sync/web_socket_manager.js.map +2 -2
- package/dist/esm/cli/aiFiles.js +17 -17
- package/dist/esm/cli/aiFiles.js.map +2 -2
- package/dist/esm/cli/configure.js +15 -10
- package/dist/esm/cli/configure.js.map +2 -2
- package/dist/esm/cli/lib/aiFiles/agentsmd.js +52 -0
- package/dist/esm/cli/lib/aiFiles/agentsmd.js.map +7 -0
- package/dist/esm/cli/lib/aiFiles/claudemd.js +52 -0
- package/dist/esm/cli/lib/aiFiles/claudemd.js.map +7 -0
- package/dist/esm/cli/lib/{ai → aiFiles}/config.js +71 -45
- package/dist/esm/cli/lib/aiFiles/config.js.map +7 -0
- package/dist/esm/cli/lib/aiFiles/cursorrules.js +16 -0
- package/dist/esm/cli/lib/aiFiles/cursorrules.js.map +7 -0
- package/dist/esm/cli/lib/aiFiles/guidelinesmd.js +28 -0
- package/dist/esm/cli/lib/aiFiles/guidelinesmd.js.map +7 -0
- package/dist/esm/cli/lib/aiFiles/index.js +210 -0
- package/dist/esm/cli/lib/aiFiles/index.js.map +7 -0
- package/dist/esm/cli/lib/aiFiles/paths.js.map +7 -0
- package/dist/esm/cli/lib/aiFiles/skills.js +147 -0
- package/dist/esm/cli/lib/aiFiles/skills.js.map +7 -0
- package/dist/esm/cli/lib/aiFiles/status.js +175 -0
- package/dist/esm/cli/lib/aiFiles/status.js.map +7 -0
- package/dist/esm/cli/lib/aiFiles/utils.js +82 -0
- package/dist/esm/cli/lib/aiFiles/utils.js.map +7 -0
- package/dist/esm/cli/lib/command.js +6 -1
- package/dist/esm/cli/lib/command.js.map +2 -2
- package/dist/esm/cli/lib/config.js +3 -4
- package/dist/esm/cli/lib/config.js.map +2 -2
- package/dist/esm/cli/lib/localDeployment/anonymous.js +2 -2
- package/dist/esm/cli/lib/localDeployment/anonymous.js.map +2 -2
- package/dist/esm/cli/lib/updates.js +8 -8
- package/dist/esm/cli/lib/updates.js.map +2 -2
- package/dist/esm/cli/lib/versionApi.js +7 -4
- package/dist/esm/cli/lib/versionApi.js.map +2 -2
- package/dist/esm/cli/lib/workos/workos.js +4 -6
- package/dist/esm/cli/lib/workos/workos.js.map +2 -2
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm-types/browser/sync/authentication_manager.d.ts.map +1 -1
- package/dist/esm-types/browser/sync/web_socket_manager.d.ts.map +1 -1
- package/dist/esm-types/cli/aiFiles.d.ts.map +1 -1
- package/dist/esm-types/cli/configure.d.ts.map +1 -1
- package/dist/esm-types/cli/lib/aiFiles/agentsmd.d.ts +19 -0
- package/dist/esm-types/cli/lib/aiFiles/agentsmd.d.ts.map +1 -0
- package/dist/esm-types/cli/lib/aiFiles/agentsmd.test.d.ts +2 -0
- package/dist/esm-types/cli/lib/aiFiles/agentsmd.test.d.ts.map +1 -0
- package/dist/esm-types/cli/lib/aiFiles/claudemd.d.ts +19 -0
- package/dist/esm-types/cli/lib/aiFiles/claudemd.d.ts.map +1 -0
- package/dist/esm-types/cli/lib/aiFiles/claudemd.test.d.ts +2 -0
- package/dist/esm-types/cli/lib/aiFiles/claudemd.test.d.ts.map +1 -0
- package/dist/esm-types/cli/lib/aiFiles/config.d.ts +46 -0
- package/dist/esm-types/cli/lib/aiFiles/config.d.ts.map +1 -0
- package/dist/esm-types/cli/lib/aiFiles/config.test.d.ts.map +1 -0
- package/dist/esm-types/cli/lib/aiFiles/cursorrules.d.ts +10 -0
- package/dist/esm-types/cli/lib/aiFiles/cursorrules.d.ts.map +1 -0
- package/dist/esm-types/cli/lib/aiFiles/guidelinesmd.d.ts +12 -0
- package/dist/esm-types/cli/lib/aiFiles/guidelinesmd.d.ts.map +1 -0
- package/dist/esm-types/cli/lib/aiFiles/guidelinesmd.test.d.ts +2 -0
- package/dist/esm-types/cli/lib/aiFiles/guidelinesmd.test.d.ts.map +1 -0
- package/dist/esm-types/cli/lib/aiFiles/index.d.ts +40 -0
- package/dist/esm-types/cli/lib/aiFiles/index.d.ts.map +1 -0
- package/dist/esm-types/cli/lib/aiFiles/index.test.d.ts.map +1 -0
- package/dist/esm-types/cli/lib/aiFiles/integration.test.d.ts.map +1 -0
- package/dist/esm-types/cli/lib/{ai → aiFiles}/paths.d.ts +4 -0
- package/dist/esm-types/cli/lib/aiFiles/paths.d.ts.map +1 -0
- package/dist/esm-types/cli/lib/aiFiles/prompt.test.d.ts.map +1 -0
- package/dist/esm-types/cli/lib/aiFiles/skills.d.ts +18 -0
- package/dist/esm-types/cli/lib/aiFiles/skills.d.ts.map +1 -0
- package/dist/esm-types/cli/lib/aiFiles/status.d.ts +3 -0
- package/dist/esm-types/cli/lib/aiFiles/status.d.ts.map +1 -0
- package/dist/esm-types/cli/lib/aiFiles/utils.d.ts +46 -0
- package/dist/esm-types/cli/lib/aiFiles/utils.d.ts.map +1 -0
- package/dist/esm-types/cli/lib/config.d.ts +1 -0
- package/dist/esm-types/cli/lib/config.d.ts.map +1 -1
- package/dist/esm-types/cli/lib/versionApi.d.ts +7 -1
- package/dist/esm-types/cli/lib/versionApi.d.ts.map +1 -1
- package/dist/esm-types/cli/lib/workos/workos.d.ts.map +1 -1
- package/dist/esm-types/index.d.ts +1 -1
- package/dist/react.bundle.js +6 -9
- package/dist/react.bundle.js.map +2 -2
- package/package.json +1 -1
- package/schemas/convex.schema.json +7 -1
- package/src/browser/sync/authentication_manager.ts +9 -4
- package/src/browser/sync/client_node.test.ts +125 -0
- package/src/browser/sync/web_socket_manager.ts +1 -7
- package/src/cli/aiFiles.ts +20 -27
- package/src/cli/configure.ts +17 -11
- package/src/cli/deploymentSelection.test.ts +56 -2
- package/src/cli/lib/{ai → aiFiles}/MANUAL_TESTING.md +6 -2
- package/src/cli/lib/aiFiles/agentsmd.test.ts +133 -0
- package/src/cli/lib/aiFiles/agentsmd.ts +77 -0
- package/src/cli/lib/aiFiles/claudemd.test.ts +92 -0
- package/src/cli/lib/aiFiles/claudemd.ts +77 -0
- package/src/cli/lib/{ai → aiFiles}/config.test.ts +181 -59
- package/src/cli/lib/{ai → aiFiles}/config.ts +92 -63
- package/src/cli/lib/aiFiles/cursorrules.ts +25 -0
- package/src/cli/lib/aiFiles/guidelinesmd.test.ts +40 -0
- package/src/cli/lib/aiFiles/guidelinesmd.ts +41 -0
- package/src/cli/lib/{ai → aiFiles}/index.test.ts +200 -339
- package/src/cli/lib/aiFiles/index.ts +303 -0
- package/src/cli/lib/{ai → aiFiles}/integration.test.ts +117 -147
- package/src/cli/lib/{ai → aiFiles}/paths.ts +5 -0
- package/src/cli/lib/{ai → aiFiles}/prompt.test.ts +78 -30
- package/src/cli/lib/aiFiles/skills.ts +213 -0
- package/src/cli/lib/aiFiles/status.ts +240 -0
- package/src/cli/lib/aiFiles/utils.ts +163 -0
- package/src/cli/lib/command.ts +6 -1
- package/src/cli/lib/config.test.ts +1 -1
- package/src/cli/lib/config.ts +6 -5
- package/src/cli/lib/localDeployment/anonymous.ts +2 -2
- package/src/cli/lib/updates.test.ts +40 -30
- package/src/cli/lib/updates.ts +8 -8
- package/src/cli/lib/versionApi.test.ts +13 -10
- package/src/cli/lib/versionApi.ts +13 -5
- package/src/cli/lib/workos/workos.ts +4 -5
- package/src/index.ts +1 -1
- package/src/values/.claude/settings.local.json +10 -0
- package/dist/cjs/cli/lib/ai/config.js.map +0 -7
- package/dist/cjs/cli/lib/ai/index.js +0 -704
- package/dist/cjs/cli/lib/ai/index.js.map +0 -7
- package/dist/cjs/cli/lib/ai/paths.js.map +0 -7
- package/dist/cjs-types/cli/lib/ai/config.d.ts +0 -50
- package/dist/cjs-types/cli/lib/ai/config.d.ts.map +0 -1
- package/dist/cjs-types/cli/lib/ai/config.test.d.ts.map +0 -1
- package/dist/cjs-types/cli/lib/ai/index.d.ts +0 -56
- package/dist/cjs-types/cli/lib/ai/index.d.ts.map +0 -1
- package/dist/cjs-types/cli/lib/ai/index.test.d.ts.map +0 -1
- package/dist/cjs-types/cli/lib/ai/integration.test.d.ts.map +0 -1
- package/dist/cjs-types/cli/lib/ai/paths.d.ts.map +0 -1
- package/dist/cjs-types/cli/lib/ai/prompt.test.d.ts.map +0 -1
- package/dist/esm/cli/lib/ai/config.js.map +0 -7
- package/dist/esm/cli/lib/ai/index.js +0 -684
- package/dist/esm/cli/lib/ai/index.js.map +0 -7
- package/dist/esm/cli/lib/ai/paths.js.map +0 -7
- package/dist/esm-types/cli/lib/ai/config.d.ts +0 -50
- package/dist/esm-types/cli/lib/ai/config.d.ts.map +0 -1
- package/dist/esm-types/cli/lib/ai/config.test.d.ts.map +0 -1
- package/dist/esm-types/cli/lib/ai/index.d.ts +0 -56
- package/dist/esm-types/cli/lib/ai/index.d.ts.map +0 -1
- package/dist/esm-types/cli/lib/ai/index.test.d.ts.map +0 -1
- package/dist/esm-types/cli/lib/ai/integration.test.d.ts.map +0 -1
- package/dist/esm-types/cli/lib/ai/paths.d.ts.map +0 -1
- package/dist/esm-types/cli/lib/ai/prompt.test.d.ts.map +0 -1
- package/src/cli/lib/ai/index.ts +0 -1006
- /package/dist/cjs/cli/lib/{ai → aiFiles}/paths.js +0 -0
- /package/dist/cjs-types/cli/lib/{ai → aiFiles}/config.test.d.ts +0 -0
- /package/dist/cjs-types/cli/lib/{ai → aiFiles}/index.test.d.ts +0 -0
- /package/dist/cjs-types/cli/lib/{ai → aiFiles}/integration.test.d.ts +0 -0
- /package/dist/cjs-types/cli/lib/{ai → aiFiles}/prompt.test.d.ts +0 -0
- /package/dist/esm/cli/lib/{ai → aiFiles}/paths.js +0 -0
- /package/dist/esm-types/cli/lib/{ai → aiFiles}/config.test.d.ts +0 -0
- /package/dist/esm-types/cli/lib/{ai → aiFiles}/index.test.d.ts +0 -0
- /package/dist/esm-types/cli/lib/{ai → aiFiles}/integration.test.d.ts +0 -0
- /package/dist/esm-types/cli/lib/{ai → aiFiles}/prompt.test.d.ts +0 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AGENTS_MD_START_MARKER,
|
|
3
|
+
AGENTS_MD_END_MARKER,
|
|
4
|
+
agentsMdConvexSection,
|
|
5
|
+
} from "../../codegen_templates/agentsmd.js";
|
|
6
|
+
import { agentsMdPath } from "./paths.js";
|
|
7
|
+
import { type AiFilesConfig } from "./config.js";
|
|
8
|
+
import {
|
|
9
|
+
type ManagedSectionTarget,
|
|
10
|
+
type InjectResult,
|
|
11
|
+
type StripResult,
|
|
12
|
+
injectManagedSection,
|
|
13
|
+
stripManagedSection,
|
|
14
|
+
hasManagedSection,
|
|
15
|
+
removeMarkdownSection,
|
|
16
|
+
} from "./utils.js";
|
|
17
|
+
|
|
18
|
+
function target(projectDir?: string): ManagedSectionTarget {
|
|
19
|
+
return {
|
|
20
|
+
filePath: agentsMdPath(projectDir),
|
|
21
|
+
startMarker: AGENTS_MD_START_MARKER,
|
|
22
|
+
endMarker: AGENTS_MD_END_MARKER,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function injectAgentsMdSection({
|
|
27
|
+
section,
|
|
28
|
+
projectDir,
|
|
29
|
+
}: {
|
|
30
|
+
section: string;
|
|
31
|
+
projectDir?: string;
|
|
32
|
+
}): Promise<InjectResult> {
|
|
33
|
+
return injectManagedSection({ ...target(projectDir), section });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function stripAgentsMdSection(
|
|
37
|
+
projectDir: string,
|
|
38
|
+
): Promise<StripResult> {
|
|
39
|
+
return stripManagedSection(target(projectDir));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export async function removeAgentsMdSection(
|
|
43
|
+
projectDir: string,
|
|
44
|
+
): Promise<boolean> {
|
|
45
|
+
return removeMarkdownSection({
|
|
46
|
+
projectDir,
|
|
47
|
+
strip: stripAgentsMdSection,
|
|
48
|
+
fileName: "AGENTS.md",
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function hasAgentsMdInstalled(
|
|
53
|
+
projectDir: string,
|
|
54
|
+
): Promise<boolean> {
|
|
55
|
+
return hasManagedSection(target(projectDir));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Inject (or update) the Convex section in AGENTS.md and record the hash.
|
|
60
|
+
* Returns true if the file was actually written.
|
|
61
|
+
*/
|
|
62
|
+
export async function applyAgentsMdSection({
|
|
63
|
+
projectDir,
|
|
64
|
+
config,
|
|
65
|
+
convexDirName,
|
|
66
|
+
}: {
|
|
67
|
+
projectDir: string;
|
|
68
|
+
config: AiFilesConfig;
|
|
69
|
+
convexDirName: string;
|
|
70
|
+
}): Promise<boolean> {
|
|
71
|
+
const result = await injectAgentsMdSection({
|
|
72
|
+
section: agentsMdConvexSection(convexDirName),
|
|
73
|
+
projectDir,
|
|
74
|
+
});
|
|
75
|
+
config.agentsMdSectionHash = result.sectionHash;
|
|
76
|
+
return result.didWrite;
|
|
77
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { describe, test, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import os from "os";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import { injectClaudeMdSection, hasClaudeMdInstalled } from "./claudemd.js";
|
|
6
|
+
import {
|
|
7
|
+
CLAUDE_MD_START_MARKER,
|
|
8
|
+
CLAUDE_MD_END_MARKER,
|
|
9
|
+
} from "../../codegen_templates/claudemd.js";
|
|
10
|
+
|
|
11
|
+
describe("injectClaudeMdSection", () => {
|
|
12
|
+
let tmpDir: string;
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
tmpDir = fs.mkdtempSync(`${os.tmpdir()}${path.sep}`);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
afterEach(() => {
|
|
19
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const section = `${CLAUDE_MD_START_MARKER}\n## Convex\nRead guidelines.\n${CLAUDE_MD_END_MARKER}`;
|
|
23
|
+
|
|
24
|
+
test("creates CLAUDE.md when it does not exist", async () => {
|
|
25
|
+
const result = await injectClaudeMdSection({ section, projectDir: tmpDir });
|
|
26
|
+
|
|
27
|
+
const content = fs.readFileSync(path.join(tmpDir, "CLAUDE.md"), "utf8");
|
|
28
|
+
expect(content).toContain(CLAUDE_MD_START_MARKER);
|
|
29
|
+
expect(content).toContain(CLAUDE_MD_END_MARKER);
|
|
30
|
+
expect(result.didWrite).toBe(true);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("appends managed section to existing CLAUDE.md content", async () => {
|
|
34
|
+
fs.writeFileSync(
|
|
35
|
+
path.join(tmpDir, "CLAUDE.md"),
|
|
36
|
+
"My custom instructions\n",
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
const result = await injectClaudeMdSection({ section, projectDir: tmpDir });
|
|
40
|
+
|
|
41
|
+
const content = fs.readFileSync(path.join(tmpDir, "CLAUDE.md"), "utf8");
|
|
42
|
+
expect(content).toContain("My custom instructions");
|
|
43
|
+
expect(content).toContain(CLAUDE_MD_START_MARKER);
|
|
44
|
+
expect(result.didWrite).toBe(true);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("replaces managed section without touching user content", async () => {
|
|
48
|
+
const oldSection = `${CLAUDE_MD_START_MARKER}\nOld\n${CLAUDE_MD_END_MARKER}`;
|
|
49
|
+
fs.writeFileSync(
|
|
50
|
+
path.join(tmpDir, "CLAUDE.md"),
|
|
51
|
+
`# Header\n\n${oldSection}\n\n# Footer\n`,
|
|
52
|
+
"utf8",
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
await injectClaudeMdSection({ section, projectDir: tmpDir });
|
|
56
|
+
|
|
57
|
+
const content = fs.readFileSync(path.join(tmpDir, "CLAUDE.md"), "utf8");
|
|
58
|
+
expect(content).toContain("# Header");
|
|
59
|
+
expect(content).toContain("# Footer");
|
|
60
|
+
expect(content).toContain("## Convex");
|
|
61
|
+
expect(content).not.toContain("Old");
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
describe("hasClaudeMdInstalled", () => {
|
|
66
|
+
let tmpDir: string;
|
|
67
|
+
|
|
68
|
+
beforeEach(() => {
|
|
69
|
+
tmpDir = fs.mkdtempSync(`${os.tmpdir()}${path.sep}`);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
afterEach(() => {
|
|
73
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("returns false when CLAUDE.md does not exist", async () => {
|
|
77
|
+
expect(await hasClaudeMdInstalled(tmpDir)).toBe(false);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("returns false when CLAUDE.md exists but has no managed markers", async () => {
|
|
81
|
+
fs.writeFileSync(path.join(tmpDir, "CLAUDE.md"), "User content only\n");
|
|
82
|
+
expect(await hasClaudeMdInstalled(tmpDir)).toBe(false);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("returns true when both markers are present", async () => {
|
|
86
|
+
fs.writeFileSync(
|
|
87
|
+
path.join(tmpDir, "CLAUDE.md"),
|
|
88
|
+
`${CLAUDE_MD_START_MARKER}\n## Convex\n${CLAUDE_MD_END_MARKER}\n`,
|
|
89
|
+
);
|
|
90
|
+
expect(await hasClaudeMdInstalled(tmpDir)).toBe(true);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CLAUDE_MD_END_MARKER,
|
|
3
|
+
CLAUDE_MD_START_MARKER,
|
|
4
|
+
claudeMdConvexSection,
|
|
5
|
+
} from "../../codegen_templates/claudemd.js";
|
|
6
|
+
import { claudeMdPath } from "./paths.js";
|
|
7
|
+
import { type AiFilesConfig } from "./config.js";
|
|
8
|
+
import {
|
|
9
|
+
type ManagedSectionTarget,
|
|
10
|
+
type InjectResult,
|
|
11
|
+
type StripResult,
|
|
12
|
+
injectManagedSection,
|
|
13
|
+
stripManagedSection,
|
|
14
|
+
hasManagedSection,
|
|
15
|
+
removeMarkdownSection,
|
|
16
|
+
} from "./utils.js";
|
|
17
|
+
|
|
18
|
+
function target(projectDir?: string): ManagedSectionTarget {
|
|
19
|
+
return {
|
|
20
|
+
filePath: claudeMdPath(projectDir),
|
|
21
|
+
startMarker: CLAUDE_MD_START_MARKER,
|
|
22
|
+
endMarker: CLAUDE_MD_END_MARKER,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function injectClaudeMdSection({
|
|
27
|
+
section,
|
|
28
|
+
projectDir,
|
|
29
|
+
}: {
|
|
30
|
+
section: string;
|
|
31
|
+
projectDir?: string;
|
|
32
|
+
}): Promise<InjectResult> {
|
|
33
|
+
return injectManagedSection({ ...target(projectDir), section });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function stripClaudeMdSection(
|
|
37
|
+
projectDir: string,
|
|
38
|
+
): Promise<StripResult> {
|
|
39
|
+
return stripManagedSection(target(projectDir));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export async function removeClaudeMdSection(
|
|
43
|
+
projectDir: string,
|
|
44
|
+
): Promise<boolean> {
|
|
45
|
+
return removeMarkdownSection({
|
|
46
|
+
projectDir,
|
|
47
|
+
strip: stripClaudeMdSection,
|
|
48
|
+
fileName: "CLAUDE.md",
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function hasClaudeMdInstalled(
|
|
53
|
+
projectDir: string,
|
|
54
|
+
): Promise<boolean> {
|
|
55
|
+
return hasManagedSection(target(projectDir));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Inject (or update) the Convex section in CLAUDE.md and record the hash.
|
|
60
|
+
* Returns true if the file was actually written.
|
|
61
|
+
*/
|
|
62
|
+
export async function applyClaudeMdSection({
|
|
63
|
+
projectDir,
|
|
64
|
+
config,
|
|
65
|
+
convexDirName,
|
|
66
|
+
}: {
|
|
67
|
+
projectDir: string;
|
|
68
|
+
config: AiFilesConfig;
|
|
69
|
+
convexDirName: string;
|
|
70
|
+
}): Promise<boolean> {
|
|
71
|
+
const result = await injectClaudeMdSection({
|
|
72
|
+
section: claudeMdConvexSection(convexDirName),
|
|
73
|
+
projectDir,
|
|
74
|
+
});
|
|
75
|
+
config.claudeMdHash = result.sectionHash;
|
|
76
|
+
return result.didWrite;
|
|
77
|
+
}
|
|
@@ -2,10 +2,11 @@ import { describe, test, expect, vi, beforeEach, afterEach } from "vitest";
|
|
|
2
2
|
import * as Sentry from "@sentry/node";
|
|
3
3
|
import { promises as fs } from "fs";
|
|
4
4
|
import {
|
|
5
|
-
|
|
5
|
+
aiFilesStateSchema,
|
|
6
|
+
hasAiFilesConfig,
|
|
6
7
|
readAiConfig,
|
|
7
8
|
writeAiConfig,
|
|
8
|
-
|
|
9
|
+
writeAiEnabledToProjectConfig,
|
|
9
10
|
} from "./config.js";
|
|
10
11
|
|
|
11
12
|
vi.mock("@sentry/node", () => ({
|
|
@@ -26,9 +27,9 @@ const mockCaptureException = vi.mocked(Sentry.captureException);
|
|
|
26
27
|
const dummyProjectDir = "/tmp/test-project";
|
|
27
28
|
const dummyConvexDir = "/tmp/test-project/convex";
|
|
28
29
|
|
|
29
|
-
describe("
|
|
30
|
+
describe("aiFilesStateSchema", () => {
|
|
30
31
|
test("accepts a fully populated valid state object", () => {
|
|
31
|
-
const result =
|
|
32
|
+
const result = aiFilesStateSchema.safeParse({
|
|
32
33
|
guidelinesHash: "abc123",
|
|
33
34
|
agentsMdSectionHash: "def456",
|
|
34
35
|
claudeMdHash: "ghi789",
|
|
@@ -39,7 +40,7 @@ describe("aiFilesSchema", () => {
|
|
|
39
40
|
});
|
|
40
41
|
|
|
41
42
|
test("accepts null hashes", () => {
|
|
42
|
-
const result =
|
|
43
|
+
const result = aiFilesStateSchema.safeParse({
|
|
43
44
|
guidelinesHash: null,
|
|
44
45
|
agentsMdSectionHash: null,
|
|
45
46
|
claudeMdHash: null,
|
|
@@ -49,7 +50,7 @@ describe("aiFilesSchema", () => {
|
|
|
49
50
|
});
|
|
50
51
|
|
|
51
52
|
test("applies default for installedSkillNames when absent", () => {
|
|
52
|
-
const result =
|
|
53
|
+
const result = aiFilesStateSchema.safeParse({
|
|
53
54
|
guidelinesHash: "abc",
|
|
54
55
|
agentsMdSectionHash: null,
|
|
55
56
|
claudeMdHash: null,
|
|
@@ -62,7 +63,7 @@ describe("aiFilesSchema", () => {
|
|
|
62
63
|
});
|
|
63
64
|
|
|
64
65
|
test("rejects a number where a string hash is expected", () => {
|
|
65
|
-
const result =
|
|
66
|
+
const result = aiFilesStateSchema.safeParse({
|
|
66
67
|
guidelinesHash: 123,
|
|
67
68
|
agentsMdSectionHash: null,
|
|
68
69
|
claudeMdHash: null,
|
|
@@ -72,7 +73,7 @@ describe("aiFilesSchema", () => {
|
|
|
72
73
|
});
|
|
73
74
|
|
|
74
75
|
test("rejects missing required fields", () => {
|
|
75
|
-
const result =
|
|
76
|
+
const result = aiFilesStateSchema.safeParse({
|
|
76
77
|
guidelinesHash: "abc",
|
|
77
78
|
});
|
|
78
79
|
expect(result.success).toBe(false);
|
|
@@ -88,13 +89,16 @@ describe("readAiConfig", () => {
|
|
|
88
89
|
Object.assign(new Error("ENOENT"), { code: "ENOENT" }),
|
|
89
90
|
);
|
|
90
91
|
|
|
91
|
-
const result = await readAiConfig(
|
|
92
|
+
const result = await readAiConfig({
|
|
93
|
+
projectDir: dummyProjectDir,
|
|
94
|
+
convexDir: dummyConvexDir,
|
|
95
|
+
});
|
|
92
96
|
|
|
93
97
|
expect(result).toBeNull();
|
|
94
98
|
expect(mockCaptureException).not.toHaveBeenCalled();
|
|
95
99
|
});
|
|
96
100
|
|
|
97
|
-
test("returns parsed state with
|
|
101
|
+
test("returns parsed state with enabled=true when convex.json is missing", async () => {
|
|
98
102
|
mockFs.readFile
|
|
99
103
|
.mockRejectedValueOnce(
|
|
100
104
|
Object.assign(new Error("ENOENT"), { code: "ENOENT" }),
|
|
@@ -108,7 +112,10 @@ describe("readAiConfig", () => {
|
|
|
108
112
|
}),
|
|
109
113
|
);
|
|
110
114
|
|
|
111
|
-
const result = await readAiConfig(
|
|
115
|
+
const result = await readAiConfig({
|
|
116
|
+
projectDir: dummyProjectDir,
|
|
117
|
+
convexDir: dummyConvexDir,
|
|
118
|
+
});
|
|
112
119
|
|
|
113
120
|
expect(result).toEqual({
|
|
114
121
|
guidelinesHash: "abc",
|
|
@@ -116,7 +123,7 @@ describe("readAiConfig", () => {
|
|
|
116
123
|
claudeMdHash: null,
|
|
117
124
|
agentSkillsSha: null,
|
|
118
125
|
installedSkillNames: [],
|
|
119
|
-
|
|
126
|
+
enabled: true,
|
|
120
127
|
});
|
|
121
128
|
});
|
|
122
129
|
|
|
@@ -129,7 +136,10 @@ describe("readAiConfig", () => {
|
|
|
129
136
|
Object.assign(new Error("ENOENT"), { code: "ENOENT" }),
|
|
130
137
|
);
|
|
131
138
|
|
|
132
|
-
const result = await readAiConfig(
|
|
139
|
+
const result = await readAiConfig({
|
|
140
|
+
projectDir: dummyProjectDir,
|
|
141
|
+
convexDir: dummyConvexDir,
|
|
142
|
+
});
|
|
133
143
|
|
|
134
144
|
expect(result).toEqual({
|
|
135
145
|
guidelinesHash: null,
|
|
@@ -137,7 +147,7 @@ describe("readAiConfig", () => {
|
|
|
137
147
|
claudeMdHash: null,
|
|
138
148
|
agentSkillsSha: null,
|
|
139
149
|
installedSkillNames: [],
|
|
140
|
-
|
|
150
|
+
enabled: false,
|
|
141
151
|
});
|
|
142
152
|
});
|
|
143
153
|
|
|
@@ -148,7 +158,10 @@ describe("readAiConfig", () => {
|
|
|
148
158
|
)
|
|
149
159
|
.mockResolvedValueOnce("not valid json {{{}");
|
|
150
160
|
|
|
151
|
-
const result = await readAiConfig(
|
|
161
|
+
const result = await readAiConfig({
|
|
162
|
+
projectDir: dummyProjectDir,
|
|
163
|
+
convexDir: dummyConvexDir,
|
|
164
|
+
});
|
|
152
165
|
|
|
153
166
|
expect(result).toBeNull();
|
|
154
167
|
expect(mockCaptureException).toHaveBeenCalledWith(expect.any(Error));
|
|
@@ -166,18 +179,21 @@ describe("readAiConfig", () => {
|
|
|
166
179
|
}),
|
|
167
180
|
);
|
|
168
181
|
|
|
169
|
-
const result = await readAiConfig(
|
|
182
|
+
const result = await readAiConfig({
|
|
183
|
+
projectDir: dummyProjectDir,
|
|
184
|
+
convexDir: dummyConvexDir,
|
|
185
|
+
});
|
|
170
186
|
|
|
171
187
|
expect(result).toBeNull();
|
|
172
188
|
expect(mockCaptureException).toHaveBeenCalledWith(expect.any(Error));
|
|
173
189
|
});
|
|
174
190
|
|
|
175
|
-
test("
|
|
191
|
+
test("reads enabled=false from legacy disableStalenessMessage field for backward compat", async () => {
|
|
176
192
|
const stored = {
|
|
177
193
|
guidelinesHash: "abc",
|
|
178
194
|
agentsMdSectionHash: "def",
|
|
179
195
|
claudeMdHash: null,
|
|
180
|
-
agentSkillsSha:
|
|
196
|
+
agentSkillsSha: null,
|
|
181
197
|
};
|
|
182
198
|
mockFs.readFile
|
|
183
199
|
.mockResolvedValueOnce(
|
|
@@ -185,13 +201,113 @@ describe("readAiConfig", () => {
|
|
|
185
201
|
)
|
|
186
202
|
.mockResolvedValueOnce(JSON.stringify(stored));
|
|
187
203
|
|
|
188
|
-
const result = await readAiConfig(
|
|
204
|
+
const result = await readAiConfig({
|
|
205
|
+
projectDir: dummyProjectDir,
|
|
206
|
+
convexDir: dummyConvexDir,
|
|
207
|
+
});
|
|
189
208
|
|
|
190
209
|
expect(result).toEqual({
|
|
191
210
|
...stored,
|
|
192
211
|
installedSkillNames: [],
|
|
193
|
-
|
|
212
|
+
enabled: false,
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
test("enabled: true takes precedence over legacy disableStalenessMessage: true", async () => {
|
|
217
|
+
const stored = {
|
|
218
|
+
guidelinesHash: "abc",
|
|
219
|
+
agentsMdSectionHash: "def",
|
|
220
|
+
claudeMdHash: null,
|
|
221
|
+
agentSkillsSha: null,
|
|
222
|
+
};
|
|
223
|
+
mockFs.readFile
|
|
224
|
+
.mockResolvedValueOnce(
|
|
225
|
+
JSON.stringify({
|
|
226
|
+
aiFiles: { enabled: true, disableStalenessMessage: true },
|
|
227
|
+
}),
|
|
228
|
+
)
|
|
229
|
+
.mockResolvedValueOnce(JSON.stringify(stored));
|
|
230
|
+
|
|
231
|
+
const result = await readAiConfig({
|
|
232
|
+
projectDir: dummyProjectDir,
|
|
233
|
+
convexDir: dummyConvexDir,
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
expect(result).toEqual({
|
|
237
|
+
...stored,
|
|
238
|
+
installedSkillNames: [],
|
|
239
|
+
enabled: true,
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
describe("hasAiFilesConfig", () => {
|
|
245
|
+
beforeEach(() => vi.clearAllMocks());
|
|
246
|
+
afterEach(() => vi.resetAllMocks());
|
|
247
|
+
|
|
248
|
+
test("returns false when neither convex.json nor state file exists", async () => {
|
|
249
|
+
mockFs.readFile.mockRejectedValue(
|
|
250
|
+
Object.assign(new Error("ENOENT"), { code: "ENOENT" }),
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
const result = await hasAiFilesConfig({
|
|
254
|
+
projectDir: dummyProjectDir,
|
|
255
|
+
convexDir: dummyConvexDir,
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
expect(result).toBe(false);
|
|
259
|
+
expect(mockCaptureException).not.toHaveBeenCalled();
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
test("returns true when convex.json disables staleness messages", async () => {
|
|
263
|
+
mockFs.readFile.mockResolvedValueOnce(
|
|
264
|
+
JSON.stringify({ aiFiles: { disableStalenessMessage: true } }),
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
const result = await hasAiFilesConfig({
|
|
268
|
+
projectDir: dummyProjectDir,
|
|
269
|
+
convexDir: dummyConvexDir,
|
|
194
270
|
});
|
|
271
|
+
|
|
272
|
+
expect(result).toBe(true);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
test("returns true when a valid state file exists", async () => {
|
|
276
|
+
mockFs.readFile
|
|
277
|
+
.mockRejectedValueOnce(
|
|
278
|
+
Object.assign(new Error("ENOENT"), { code: "ENOENT" }),
|
|
279
|
+
)
|
|
280
|
+
.mockResolvedValueOnce(
|
|
281
|
+
JSON.stringify({
|
|
282
|
+
guidelinesHash: "abc",
|
|
283
|
+
agentsMdSectionHash: "def",
|
|
284
|
+
claudeMdHash: null,
|
|
285
|
+
agentSkillsSha: null,
|
|
286
|
+
}),
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
const result = await hasAiFilesConfig({
|
|
290
|
+
projectDir: dummyProjectDir,
|
|
291
|
+
convexDir: dummyConvexDir,
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
expect(result).toBe(true);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
test("returns false and captures exception when the state file is invalid", async () => {
|
|
298
|
+
mockFs.readFile
|
|
299
|
+
.mockRejectedValueOnce(
|
|
300
|
+
Object.assign(new Error("ENOENT"), { code: "ENOENT" }),
|
|
301
|
+
)
|
|
302
|
+
.mockResolvedValueOnce("not valid json {{{}");
|
|
303
|
+
|
|
304
|
+
const result = await hasAiFilesConfig({
|
|
305
|
+
projectDir: dummyProjectDir,
|
|
306
|
+
convexDir: dummyConvexDir,
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
expect(result).toBe(false);
|
|
310
|
+
expect(mockCaptureException).toHaveBeenCalledWith(expect.any(Error));
|
|
195
311
|
});
|
|
196
312
|
});
|
|
197
313
|
|
|
@@ -199,11 +315,11 @@ describe("writeAiConfig", () => {
|
|
|
199
315
|
beforeEach(() => vi.clearAllMocks());
|
|
200
316
|
afterEach(() => vi.resetAllMocks());
|
|
201
317
|
|
|
202
|
-
test("writes state file and does not persist
|
|
318
|
+
test("writes state file and does not persist enabled=true by default", async () => {
|
|
203
319
|
mockFs.writeFile.mockResolvedValue(undefined);
|
|
204
320
|
|
|
205
321
|
const config = {
|
|
206
|
-
|
|
322
|
+
enabled: true,
|
|
207
323
|
guidelinesHash: "abc",
|
|
208
324
|
agentsMdSectionHash: "def",
|
|
209
325
|
claudeMdHash: null,
|
|
@@ -211,7 +327,11 @@ describe("writeAiConfig", () => {
|
|
|
211
327
|
installedSkillNames: ["convex-migrations"],
|
|
212
328
|
};
|
|
213
329
|
|
|
214
|
-
await writeAiConfig(
|
|
330
|
+
await writeAiConfig({
|
|
331
|
+
config,
|
|
332
|
+
projectDir: dummyProjectDir,
|
|
333
|
+
convexDir: dummyConvexDir,
|
|
334
|
+
});
|
|
215
335
|
|
|
216
336
|
expect(mockFs.writeFile).toHaveBeenCalledTimes(1);
|
|
217
337
|
expect(mockFs.writeFile).toHaveBeenCalledWith(
|
|
@@ -231,23 +351,23 @@ describe("writeAiConfig", () => {
|
|
|
231
351
|
);
|
|
232
352
|
});
|
|
233
353
|
|
|
234
|
-
test("persists
|
|
354
|
+
test("persists enabled=true when requested explicitly", async () => {
|
|
235
355
|
mockFs.writeFile.mockResolvedValue(undefined);
|
|
236
356
|
mockFs.readFile.mockResolvedValue("{}");
|
|
237
357
|
|
|
238
|
-
await writeAiConfig(
|
|
239
|
-
{
|
|
240
|
-
|
|
358
|
+
await writeAiConfig({
|
|
359
|
+
config: {
|
|
360
|
+
enabled: true,
|
|
241
361
|
guidelinesHash: null,
|
|
242
362
|
agentsMdSectionHash: null,
|
|
243
363
|
claudeMdHash: null,
|
|
244
364
|
agentSkillsSha: null,
|
|
245
365
|
installedSkillNames: [],
|
|
246
366
|
},
|
|
247
|
-
dummyProjectDir,
|
|
248
|
-
dummyConvexDir,
|
|
249
|
-
{
|
|
250
|
-
);
|
|
367
|
+
projectDir: dummyProjectDir,
|
|
368
|
+
convexDir: dummyConvexDir,
|
|
369
|
+
options: { persistEnabledPreference: "always" },
|
|
370
|
+
});
|
|
251
371
|
|
|
252
372
|
expect(mockFs.writeFile).toHaveBeenNthCalledWith(
|
|
253
373
|
2,
|
|
@@ -255,9 +375,7 @@ describe("writeAiConfig", () => {
|
|
|
255
375
|
JSON.stringify(
|
|
256
376
|
{
|
|
257
377
|
$schema: "node_modules/convex/schemas/convex.schema.json",
|
|
258
|
-
aiFiles: {
|
|
259
|
-
disableStalenessMessage: false,
|
|
260
|
-
},
|
|
378
|
+
aiFiles: { enabled: true },
|
|
261
379
|
},
|
|
262
380
|
null,
|
|
263
381
|
2,
|
|
@@ -266,48 +384,48 @@ describe("writeAiConfig", () => {
|
|
|
266
384
|
);
|
|
267
385
|
});
|
|
268
386
|
|
|
269
|
-
test("persists to convex.json by default
|
|
387
|
+
test("persists enabled=false to convex.json by default", async () => {
|
|
270
388
|
mockFs.writeFile.mockResolvedValue(undefined);
|
|
271
389
|
mockFs.readFile.mockResolvedValue("{}");
|
|
272
390
|
|
|
273
|
-
await writeAiConfig(
|
|
274
|
-
{
|
|
275
|
-
|
|
391
|
+
await writeAiConfig({
|
|
392
|
+
config: {
|
|
393
|
+
enabled: false,
|
|
276
394
|
guidelinesHash: null,
|
|
277
395
|
agentsMdSectionHash: null,
|
|
278
396
|
claudeMdHash: null,
|
|
279
397
|
agentSkillsSha: null,
|
|
280
398
|
installedSkillNames: [],
|
|
281
399
|
},
|
|
282
|
-
dummyProjectDir,
|
|
283
|
-
dummyConvexDir,
|
|
284
|
-
);
|
|
400
|
+
projectDir: dummyProjectDir,
|
|
401
|
+
convexDir: dummyConvexDir,
|
|
402
|
+
});
|
|
285
403
|
|
|
286
404
|
expect(mockFs.writeFile).toHaveBeenCalledTimes(2);
|
|
287
405
|
expect(mockFs.writeFile).toHaveBeenNthCalledWith(
|
|
288
406
|
2,
|
|
289
407
|
expect.stringContaining("convex.json"),
|
|
290
|
-
expect.stringContaining('"
|
|
408
|
+
expect.stringContaining('"enabled": false'),
|
|
291
409
|
"utf8",
|
|
292
410
|
);
|
|
293
411
|
});
|
|
294
412
|
|
|
295
|
-
test("never writes to convex.json when
|
|
413
|
+
test("never writes to convex.json when persistEnabledPreference is 'never'", async () => {
|
|
296
414
|
mockFs.writeFile.mockResolvedValue(undefined);
|
|
297
415
|
|
|
298
|
-
await writeAiConfig(
|
|
299
|
-
{
|
|
300
|
-
|
|
416
|
+
await writeAiConfig({
|
|
417
|
+
config: {
|
|
418
|
+
enabled: false,
|
|
301
419
|
guidelinesHash: null,
|
|
302
420
|
agentsMdSectionHash: null,
|
|
303
421
|
claudeMdHash: null,
|
|
304
422
|
agentSkillsSha: null,
|
|
305
423
|
installedSkillNames: [],
|
|
306
424
|
},
|
|
307
|
-
dummyProjectDir,
|
|
308
|
-
dummyConvexDir,
|
|
309
|
-
{
|
|
310
|
-
);
|
|
425
|
+
projectDir: dummyProjectDir,
|
|
426
|
+
convexDir: dummyConvexDir,
|
|
427
|
+
options: { persistEnabledPreference: "never" },
|
|
428
|
+
});
|
|
311
429
|
|
|
312
430
|
expect(mockFs.writeFile).toHaveBeenCalledTimes(1);
|
|
313
431
|
expect(mockFs.writeFile).toHaveBeenCalledWith(
|
|
@@ -318,21 +436,25 @@ describe("writeAiConfig", () => {
|
|
|
318
436
|
});
|
|
319
437
|
});
|
|
320
438
|
|
|
321
|
-
describe("
|
|
439
|
+
describe("writeAiEnabledToProjectConfig", () => {
|
|
322
440
|
beforeEach(() => vi.clearAllMocks());
|
|
323
441
|
afterEach(() => vi.resetAllMocks());
|
|
324
442
|
|
|
325
|
-
test("writes
|
|
326
|
-
mockFs.readFile.mockResolvedValue(
|
|
443
|
+
test("writes enabled=false to convex.json and drops legacy disableStalenessMessage", async () => {
|
|
444
|
+
mockFs.readFile.mockResolvedValue(
|
|
445
|
+
JSON.stringify({ aiFiles: { disableStalenessMessage: true } }),
|
|
446
|
+
);
|
|
327
447
|
mockFs.writeFile.mockResolvedValue(undefined);
|
|
328
448
|
|
|
329
|
-
await
|
|
449
|
+
await writeAiEnabledToProjectConfig({
|
|
450
|
+
projectDir: dummyProjectDir,
|
|
451
|
+
enabled: false,
|
|
452
|
+
});
|
|
330
453
|
|
|
331
454
|
expect(mockFs.writeFile).toHaveBeenCalledTimes(1);
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
);
|
|
455
|
+
const written = mockFs.writeFile.mock.calls[0][1] as string;
|
|
456
|
+
const parsed = JSON.parse(written);
|
|
457
|
+
expect(parsed.aiFiles.enabled).toBe(false);
|
|
458
|
+
expect(parsed.aiFiles.disableStalenessMessage).toBeUndefined();
|
|
337
459
|
});
|
|
338
460
|
});
|