@popoverai/dotrequirements 0.23.0 → 0.24.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 (235) hide show
  1. package/README.md +169 -22
  2. package/dist/cli.js +121 -60
  3. package/dist/codebase-to-spec/budget.d.ts +53 -0
  4. package/dist/codebase-to-spec/budget.js +80 -0
  5. package/dist/codebase-to-spec/cache.d.ts +49 -0
  6. package/dist/codebase-to-spec/cache.js +54 -0
  7. package/dist/codebase-to-spec/claude.d.ts +69 -0
  8. package/dist/codebase-to-spec/claude.js +126 -0
  9. package/dist/codebase-to-spec/compose.d.ts +49 -0
  10. package/dist/codebase-to-spec/compose.js +124 -0
  11. package/dist/codebase-to-spec/edit-loop.d.ts +54 -0
  12. package/dist/codebase-to-spec/edit-loop.js +195 -0
  13. package/dist/codebase-to-spec/editor.d.ts +54 -0
  14. package/dist/codebase-to-spec/editor.js +74 -0
  15. package/dist/codebase-to-spec/exit-codes.d.ts +40 -0
  16. package/dist/codebase-to-spec/exit-codes.js +58 -0
  17. package/dist/codebase-to-spec/fan-out.d.ts +63 -0
  18. package/dist/codebase-to-spec/fan-out.js +215 -0
  19. package/dist/codebase-to-spec/interactive.d.ts +30 -0
  20. package/dist/codebase-to-spec/interactive.js +48 -0
  21. package/dist/codebase-to-spec/outline-review-loop.d.ts +51 -0
  22. package/dist/codebase-to-spec/outline-review-loop.js +187 -0
  23. package/dist/codebase-to-spec/pack.d.ts +51 -0
  24. package/dist/codebase-to-spec/pack.js +127 -0
  25. package/dist/codebase-to-spec/planner.d.ts +41 -0
  26. package/dist/codebase-to-spec/planner.js +76 -0
  27. package/dist/codebase-to-spec/present.d.ts +94 -0
  28. package/dist/codebase-to-spec/present.js +288 -0
  29. package/dist/codebase-to-spec/progress.d.ts +33 -0
  30. package/dist/codebase-to-spec/progress.js +28 -0
  31. package/dist/codebase-to-spec/prompts/editor.d.ts +13 -0
  32. package/dist/codebase-to-spec/prompts/editor.js +57 -0
  33. package/dist/codebase-to-spec/prompts/outline-reviewer.d.ts +12 -0
  34. package/dist/codebase-to-spec/prompts/outline-reviewer.js +87 -0
  35. package/dist/codebase-to-spec/prompts/planner-initial.d.ts +11 -0
  36. package/dist/codebase-to-spec/prompts/planner-initial.js +125 -0
  37. package/dist/codebase-to-spec/prompts/planner-revise.d.ts +14 -0
  38. package/dist/codebase-to-spec/prompts/planner-revise.js +60 -0
  39. package/dist/codebase-to-spec/prompts/spec-reviewer.d.ts +16 -0
  40. package/dist/codebase-to-spec/prompts/spec-reviewer.js +96 -0
  41. package/dist/codebase-to-spec/prompts/specifier.d.ts +12 -0
  42. package/dist/codebase-to-spec/prompts/specifier.js +100 -0
  43. package/dist/codebase-to-spec/prompts/style-check.d.ts +12 -0
  44. package/dist/codebase-to-spec/prompts/style-check.js +78 -0
  45. package/dist/codebase-to-spec/schemas.d.ts +257 -0
  46. package/dist/codebase-to-spec/schemas.js +183 -0
  47. package/dist/codebase-to-spec/skill-install.d.ts +57 -0
  48. package/dist/codebase-to-spec/skill-install.js +79 -0
  49. package/dist/codebase-to-spec/slice.d.ts +49 -0
  50. package/dist/codebase-to-spec/slice.js +111 -0
  51. package/dist/codebase-to-spec/specifier.d.ts +60 -0
  52. package/dist/codebase-to-spec/specifier.js +79 -0
  53. package/dist/codebase-to-spec/style-check.d.ts +29 -0
  54. package/dist/codebase-to-spec/style-check.js +33 -0
  55. package/dist/codebase-to-spec/summary.d.ts +51 -0
  56. package/dist/codebase-to-spec/summary.js +183 -0
  57. package/dist/codebase-to-spec/validate.d.ts +46 -0
  58. package/dist/codebase-to-spec/validate.js +130 -0
  59. package/dist/commands/acceptance-test.d.ts +6 -0
  60. package/dist/commands/{browsertest.js → acceptance-test.js} +36 -29
  61. package/dist/commands/ai-setup.d.ts +5 -0
  62. package/dist/commands/ai-setup.js +441 -0
  63. package/dist/commands/codebase-to-spec/compose.d.ts +14 -0
  64. package/dist/commands/codebase-to-spec/compose.js +57 -0
  65. package/dist/commands/codebase-to-spec/edit-loop.d.ts +16 -0
  66. package/dist/commands/codebase-to-spec/edit-loop.js +83 -0
  67. package/dist/commands/codebase-to-spec/fan-out.d.ts +19 -0
  68. package/dist/commands/codebase-to-spec/fan-out.js +77 -0
  69. package/dist/commands/codebase-to-spec/index.d.ts +9 -0
  70. package/dist/commands/codebase-to-spec/index.js +135 -0
  71. package/dist/commands/codebase-to-spec/pack.d.ts +22 -0
  72. package/dist/commands/codebase-to-spec/pack.js +76 -0
  73. package/dist/commands/codebase-to-spec/plan-loop.d.ts +26 -0
  74. package/dist/commands/codebase-to-spec/plan-loop.js +105 -0
  75. package/dist/commands/codebase-to-spec/present.d.ts +21 -0
  76. package/dist/commands/codebase-to-spec/present.js +92 -0
  77. package/dist/commands/codebase-to-spec/run.d.ts +20 -0
  78. package/dist/commands/codebase-to-spec/run.js +85 -0
  79. package/dist/commands/codebase-to-spec/skill-install.d.ts +20 -0
  80. package/dist/commands/codebase-to-spec/skill-install.js +51 -0
  81. package/dist/commands/codebase-to-spec/specify-area.d.ts +18 -0
  82. package/dist/commands/codebase-to-spec/specify-area.js +82 -0
  83. package/dist/commands/codebase-to-spec/style-check.d.ts +15 -0
  84. package/dist/commands/codebase-to-spec/style-check.js +42 -0
  85. package/dist/commands/codebase-to-spec/validate.d.ts +18 -0
  86. package/dist/commands/codebase-to-spec/validate.js +38 -0
  87. package/dist/commands/create-requirement-document.d.ts +2 -0
  88. package/dist/commands/create-requirement-document.js +41 -0
  89. package/dist/commands/finalize.js +7 -7
  90. package/dist/commands/get.d.ts +2 -0
  91. package/dist/commands/get.js +55 -0
  92. package/dist/commands/init.js +132 -117
  93. package/dist/commands/link.js +27 -27
  94. package/dist/commands/list.d.ts +6 -0
  95. package/dist/commands/list.js +43 -0
  96. package/dist/commands/mcp.js +1 -1
  97. package/dist/commands/prepare.js +4 -4
  98. package/dist/commands/pull.js +116 -121
  99. package/dist/commands/push.js +106 -112
  100. package/dist/commands/report.d.ts +6 -2
  101. package/dist/commands/report.js +177 -122
  102. package/dist/commands/requirements-for.d.ts +2 -0
  103. package/dist/commands/requirements-for.js +29 -0
  104. package/dist/commands/review-test.d.ts +2 -0
  105. package/dist/commands/review-test.js +75 -0
  106. package/dist/commands/search.d.ts +6 -0
  107. package/dist/commands/search.js +39 -0
  108. package/dist/commands/style-check.d.ts +7 -0
  109. package/dist/commands/style-check.js +75 -0
  110. package/dist/commands/tests-for.d.ts +2 -0
  111. package/dist/commands/tests-for.js +80 -0
  112. package/dist/commands/validate.d.ts +6 -0
  113. package/dist/commands/validate.js +72 -0
  114. package/dist/config.js +1 -1
  115. package/dist/convex.d.ts +34 -22
  116. package/dist/convex.js +38 -22
  117. package/dist/harness/cache.d.ts +1 -5
  118. package/dist/harness/cache.js +49 -59
  119. package/dist/harness/convexReporting.d.ts +1 -1
  120. package/dist/harness/convexReporting.js +9 -7
  121. package/dist/harness/coverageCache.js +3 -3
  122. package/dist/harness/finalize.js +59 -46
  123. package/dist/harness/index.d.ts +6 -7
  124. package/dist/harness/index.js +9 -10
  125. package/dist/harness/prepare.js +6 -5
  126. package/dist/harness/requirementsLoader.d.ts +2 -2
  127. package/dist/harness/requirementsLoader.js +13 -35
  128. package/dist/harness/tracking.js +18 -18
  129. package/dist/harness/types.d.ts +1 -1
  130. package/dist/mcp/convexClient.d.ts +0 -39
  131. package/dist/mcp/convexClient.js +2 -107
  132. package/dist/mcp/handlers/authoring.d.ts +1 -1
  133. package/dist/mcp/handlers/authoring.js +30 -234
  134. package/dist/mcp/handlers/debug.d.ts +2 -3
  135. package/dist/mcp/handlers/debug.js +10 -10
  136. package/dist/mcp/handlers/get.d.ts +1 -1
  137. package/dist/mcp/handlers/get.js +11 -10
  138. package/dist/mcp/handlers/index.d.ts +20 -20
  139. package/dist/mcp/handlers/index.js +10 -10
  140. package/dist/mcp/handlers/list.d.ts +4 -33
  141. package/dist/mcp/handlers/list.js +16 -38
  142. package/dist/mcp/handlers/push.d.ts +1 -1
  143. package/dist/mcp/handlers/push.js +28 -18
  144. package/dist/mcp/handlers/report.d.ts +16 -0
  145. package/dist/mcp/handlers/report.js +134 -0
  146. package/dist/mcp/handlers/review.d.ts +1 -1
  147. package/dist/mcp/handlers/review.js +40 -59
  148. package/dist/mcp/handlers/search.d.ts +1 -1
  149. package/dist/mcp/handlers/search.js +7 -9
  150. package/dist/mcp/handlers/test-mapping.d.ts +1 -1
  151. package/dist/mcp/handlers/test-mapping.js +14 -14
  152. package/dist/mcp/handlers/types.d.ts +3 -3
  153. package/dist/mcp/handlers/types.js +2 -2
  154. package/dist/mcp/index.d.ts +1 -1
  155. package/dist/mcp/index.js +147 -167
  156. package/dist/push/core.d.ts +2 -2
  157. package/dist/push/core.js +20 -20
  158. package/dist/push/index.d.ts +1 -1
  159. package/dist/push/index.js +2 -2
  160. package/dist/requirements/cloud-ai.d.ts +57 -0
  161. package/dist/requirements/cloud-ai.js +104 -0
  162. package/dist/requirements/cloud-coverage.d.ts +41 -0
  163. package/dist/requirements/cloud-coverage.js +60 -0
  164. package/dist/requirements/coverage.d.ts +45 -0
  165. package/dist/requirements/coverage.js +114 -0
  166. package/dist/{mcp → requirements}/grep.d.ts +10 -1
  167. package/dist/{mcp → requirements}/grep.js +89 -44
  168. package/dist/{mcp/requirements.d.ts → requirements/index.d.ts} +19 -3
  169. package/dist/{mcp/requirements.js → requirements/index.js} +54 -35
  170. package/dist/requirements/style-guide.d.ts +67 -0
  171. package/dist/requirements/style-guide.js +299 -0
  172. package/dist/{mcp → requirements}/testCodeExtractor.js +24 -26
  173. package/dist/schema/browser.d.ts +8 -8
  174. package/dist/schema/browser.js +13 -15
  175. package/dist/schema/builder.d.ts +1 -1
  176. package/dist/schema/builder.js +13 -44
  177. package/dist/schema/conversions.d.ts +2 -2
  178. package/dist/schema/conversions.js +11 -11
  179. package/dist/schema/index.d.ts +9 -9
  180. package/dist/schema/index.js +15 -15
  181. package/dist/schema/parser-core.d.ts +1 -1
  182. package/dist/schema/parser-core.js +23 -22
  183. package/dist/schema/parser.d.ts +3 -3
  184. package/dist/schema/parser.js +27 -31
  185. package/dist/schema/resolver.d.ts +1 -1
  186. package/dist/schema/resolver.js +9 -9
  187. package/dist/schema/scenario.d.ts +1 -1
  188. package/dist/schema/scenario.js +1 -1
  189. package/dist/schema/schemas.d.ts +3 -3
  190. package/dist/schema/schemas.js +41 -28
  191. package/dist/schema/test-schema.js +27 -27
  192. package/dist/templates/context-file-section.md +3 -2
  193. package/dist/templates/example-requirements.js +1 -1
  194. package/dist/templates/example-requirements.ts +3 -1
  195. package/dist/templates/requirements-readme.js +1 -1
  196. package/dist/templates/requirements-readme.ts +1 -1
  197. package/dist/templates/skills/codebase-to-spec/SKILL.md +118 -0
  198. package/dist/utils/brand.js +3 -3
  199. package/dist/utils/browser-launch.js +4 -4
  200. package/dist/utils/context-file.d.ts +1 -1
  201. package/dist/utils/context-file.js +26 -26
  202. package/dist/utils/env.js +7 -7
  203. package/dist/utils/gitignore.js +7 -7
  204. package/dist/utils/oauth-callback-server.d.ts +1 -1
  205. package/dist/utils/oauth-callback-server.js +27 -25
  206. package/dist/utils/oauth-flow.js +32 -29
  207. package/dist/utils/project-discovery.d.ts +3 -3
  208. package/dist/utils/project-discovery.js +18 -17
  209. package/dist/utils/project-name.js +8 -8
  210. package/dist/utils/project-selector.d.ts +1 -1
  211. package/dist/utils/project-selector.js +24 -21
  212. package/dist/utils/project-settings.d.ts +1 -1
  213. package/dist/utils/project-settings.js +24 -22
  214. package/dist/utils/templates.js +6 -6
  215. package/package.json +3 -2
  216. package/dist/commands/browsertest.d.ts +0 -6
  217. package/dist/commands/login.d.ts +0 -12
  218. package/dist/commands/login.js +0 -117
  219. package/dist/commands/logout.d.ts +0 -5
  220. package/dist/commands/logout.js +0 -17
  221. package/dist/commands/mcp-setup.d.ts +0 -5
  222. package/dist/commands/mcp-setup.js +0 -431
  223. package/dist/commands/test.d.ts +0 -6
  224. package/dist/commands/test.js +0 -78
  225. package/dist/mcp/handlers/coverage.d.ts +0 -44
  226. package/dist/mcp/handlers/coverage.js +0 -105
  227. package/dist/mcp/types.d.ts +0 -27
  228. package/dist/mcp/types.js +0 -2
  229. package/dist/utils/local-project.d.ts +0 -31
  230. package/dist/utils/local-project.js +0 -33
  231. package/dist/utils/token-refresh.d.ts +0 -24
  232. package/dist/utils/token-refresh.js +0 -69
  233. package/dist/utils/token-storage.d.ts +0 -31
  234. package/dist/utils/token-storage.js +0 -57
  235. /package/dist/{mcp → requirements}/testCodeExtractor.d.ts +0 -0
@@ -0,0 +1,80 @@
1
+ import { existsSync } from "node:fs";
2
+ import { relative, resolve } from "node:path";
3
+ import { findAllTestReferences } from "../requirements/grep.js";
4
+ import { loadAllRequirements } from "../requirements/index.js";
5
+ export async function testsForCommand(requirementsFile) {
6
+ const workspaceRoot = process.cwd();
7
+ const fullPath = resolve(workspaceRoot, requirementsFile);
8
+ // CLI-FIND-MAP-2.3: missing file or wrong extension → error + non-zero exit
9
+ if (!existsSync(fullPath)) {
10
+ throw new Error(`Requirements file not found: ${requirementsFile}`);
11
+ }
12
+ if (!requirementsFile.endsWith(".requirements.md")) {
13
+ throw new Error(`File must be a requirements file: *.requirements.md`);
14
+ }
15
+ const { flattened } = await loadAllRequirements(workspaceRoot);
16
+ const fileRequirements = flattened.filter((req) => resolve(workspaceRoot, req.sourceFile) === fullPath);
17
+ if (fileRequirements.length === 0) {
18
+ console.log(`No requirements found in "${requirementsFile}".`);
19
+ return;
20
+ }
21
+ const rootIds = new Set(fileRequirements.map((req) => req.rootId));
22
+ const allTestRefs = await findAllTestReferences(workspaceRoot);
23
+ // Build coverage map: requirement ID → test refs
24
+ const coverageMap = new Map();
25
+ for (const ref of allTestRefs) {
26
+ if (!coverageMap.has(ref.requirementId)) {
27
+ coverageMap.set(ref.requirementId, []);
28
+ }
29
+ coverageMap.get(ref.requirementId).push(ref);
30
+ }
31
+ // CLI-FIND-MAP-2.0: group by coverage status
32
+ const covered = [];
33
+ const notCovered = [];
34
+ for (const rootId of rootIds) {
35
+ const reqAndChildren = fileRequirements.filter((r) => r.rootId === rootId);
36
+ const allIds = reqAndChildren.map((r) => r.id);
37
+ const testsForThisReq = [];
38
+ for (const id of allIds) {
39
+ const refs = coverageMap.get(id);
40
+ if (refs) {
41
+ testsForThisReq.push(...refs);
42
+ }
43
+ }
44
+ if (testsForThisReq.length > 0) {
45
+ covered.push({ id: rootId, tests: testsForThisReq });
46
+ }
47
+ else {
48
+ notCovered.push(rootId);
49
+ }
50
+ }
51
+ console.log(`Test coverage for "${requirementsFile}":\n`);
52
+ if (covered.length > 0) {
53
+ console.log(`Covered (${covered.length}):`);
54
+ // CLI-FIND-MAP-2.1: test file path + line numbers for each covered req
55
+ for (const { id, tests } of covered) {
56
+ const testsByFile = new Map();
57
+ for (const test of tests) {
58
+ if (!testsByFile.has(test.file)) {
59
+ testsByFile.set(test.file, []);
60
+ }
61
+ testsByFile.get(test.file).push(test.line);
62
+ }
63
+ console.log(` ${id}: ${tests.length} test${tests.length === 1 ? "" : "s"}`);
64
+ for (const [file, lines] of testsByFile) {
65
+ const relPath = relative(workspaceRoot, file);
66
+ const lineList = lines.sort((a, b) => a - b).join(", ");
67
+ console.log(` ${relPath} (lines ${lineList})`);
68
+ }
69
+ }
70
+ console.log();
71
+ }
72
+ // CLI-FIND-MAP-2.2: uncovered group
73
+ if (notCovered.length > 0) {
74
+ console.log(`Not Covered (${notCovered.length}):`);
75
+ for (const id of notCovered) {
76
+ console.log(` ${id}: No tests found`);
77
+ }
78
+ }
79
+ }
80
+ //# sourceMappingURL=tests-for.js.map
@@ -0,0 +1,6 @@
1
+ interface ValidateOptions {
2
+ file?: string;
3
+ }
4
+ export declare function validateCommand(options: ValidateOptions): Promise<void>;
5
+ export {};
6
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1,72 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { parseRequirementsFromFile, } from "../schema/index.js";
4
+ export async function validateCommand(options) {
5
+ console.log("Parsing and validating requirements...");
6
+ const requirementsDir = path.join(process.cwd(), ".requirements");
7
+ if (!fs.existsSync(requirementsDir)) {
8
+ console.error("Error: .requirements/ directory not found.");
9
+ console.log('Run "dotrequirements pull" first to sync requirements.');
10
+ process.exit(1);
11
+ }
12
+ // Get files to test
13
+ let files;
14
+ if (options.file) {
15
+ files = [options.file];
16
+ }
17
+ else {
18
+ files = fs
19
+ .readdirSync(requirementsDir)
20
+ .filter((f) => f.endsWith(".requirements.md"))
21
+ .map((f) => path.join(requirementsDir, f));
22
+ }
23
+ if (files.length === 0) {
24
+ console.log("No Markdown requirements files found to test.");
25
+ return;
26
+ }
27
+ let totalRequirements = 0;
28
+ let passCount = 0;
29
+ let failCount = 0;
30
+ for (const filePath of files) {
31
+ const fileName = path.basename(filePath);
32
+ console.log(`\nTesting: ${fileName}`);
33
+ try {
34
+ // Parse and validate using shared schema module
35
+ const { metadata, requirements } = parseRequirementsFromFile(filePath);
36
+ // Count total requirements (root + all descendants)
37
+ let count = 0;
38
+ for (const req of requirements) {
39
+ count += countRequirementsInTree(req);
40
+ }
41
+ totalRequirements += count;
42
+ console.log(` ✓ Valid structure (${count} requirement(s))`);
43
+ console.log(` ✓ Version: ${metadata.version}`);
44
+ console.log(` ✓ Last sync: ${metadata.pulledAt}`);
45
+ passCount++;
46
+ }
47
+ catch (error) {
48
+ console.error(` ✗ Validation failed:`, error);
49
+ failCount++;
50
+ }
51
+ }
52
+ console.log(`\n${"=".repeat(50)}`);
53
+ console.log(`Total files: ${files.length}`);
54
+ console.log(`Passed: ${passCount}`);
55
+ console.log(`Failed: ${failCount}`);
56
+ console.log(`Total requirements: ${totalRequirements}`);
57
+ if (failCount > 0) {
58
+ process.exit(1);
59
+ }
60
+ }
61
+ /**
62
+ * Recursively count requirements in a tree (1 for root + all descendants)
63
+ */
64
+ function countRequirementsInTree(node) {
65
+ let count = 1; // Count this node
66
+ // Count all children
67
+ for (const child of node.children) {
68
+ count += countRequirementsInTree(child);
69
+ }
70
+ return count;
71
+ }
72
+ //# sourceMappingURL=validate.js.map
package/dist/config.js CHANGED
@@ -2,7 +2,7 @@
2
2
  * Production Convex deployment URL
3
3
  * All users connect to the same backend
4
4
  */
5
- const CONVEX_URL = 'https://data.dotrequirements.io';
5
+ const CONVEX_URL = "https://data.dotrequirements.io";
6
6
  /**
7
7
  * Get the Convex deployment URL
8
8
  */
package/dist/convex.d.ts CHANGED
@@ -1,66 +1,78 @@
1
1
  /**
2
- * Convex API stub for CLI usage
3
- * This will be replaced with actual generated types when linked to the Convex backend
2
+ * Convex API stub for CLI usage.
3
+ *
4
+ * The CLI doesn't import generated convex types (those live in the convex
5
+ * package and aren't shipped to npm consumers), so we hand-roll the `api`
6
+ * object that gets passed into `ConvexHttpClient.query/mutation/action`.
7
+ *
8
+ * `makeFunctionReference<"query" | "mutation" | "action">("path:fn")` is the
9
+ * official escape hatch for this exact case — it builds a `FunctionReference`
10
+ * with the right shape for the convex client to consume, without us having to
11
+ * cast through `any`.
12
+ *
13
+ * The args/return types default to `any` inside the convex library types
14
+ * (that's `FunctionReference`'s public surface), but at the call site we still
15
+ * get a properly typed reference object — no user-authored `any` here.
4
16
  */
5
17
  export declare const api: {
6
18
  documents: {
7
19
  queries: {
8
- exportForCli: any;
9
- exportProjectForCli: any;
10
- checkDocumentsExist: any;
11
- getDocumentsMetadata: any;
20
+ exportForCli: import("convex/server").FunctionReference<"query", "public", any, any, string | undefined>;
21
+ exportProjectForCli: import("convex/server").FunctionReference<"query", "public", any, any, string | undefined>;
22
+ checkDocumentsExist: import("convex/server").FunctionReference<"query", "public", any, any, string | undefined>;
23
+ getDocumentsMetadata: import("convex/server").FunctionReference<"query", "public", any, any, string | undefined>;
12
24
  };
13
25
  mutations: {
14
- create: any;
15
- update: any;
26
+ create: import("convex/server").FunctionReference<"mutation", "public", any, any, string | undefined>;
27
+ update: import("convex/server").FunctionReference<"mutation", "public", any, any, string | undefined>;
16
28
  };
17
29
  saveWithRequirements: {
18
- saveWithRequirements: any;
30
+ saveWithRequirements: import("convex/server").FunctionReference<"mutation", "public", any, any, string | undefined>;
19
31
  };
20
32
  };
21
33
  requirements: {
22
34
  queries: {
23
- listByProject: any;
35
+ listByProject: import("convex/server").FunctionReference<"query", "public", any, any, string | undefined>;
24
36
  };
25
37
  mutations: {
26
- bulkUpdate: any;
38
+ bulkUpdate: import("convex/server").FunctionReference<"mutation", "public", any, any, string | undefined>;
27
39
  };
28
40
  };
29
41
  projects: {
30
42
  mutations: {
31
- create: any;
43
+ create: import("convex/server").FunctionReference<"mutation", "public", any, any, string | undefined>;
32
44
  };
33
45
  };
34
46
  teams: {
35
47
  queries: {
36
- listForUserWithUsage: any;
48
+ listForUserWithUsage: import("convex/server").FunctionReference<"query", "public", any, any, string | undefined>;
37
49
  };
38
50
  mutations: {
39
- ensureDefaultTeam: any;
51
+ ensureDefaultTeam: import("convex/server").FunctionReference<"mutation", "public", any, any, string | undefined>;
40
52
  };
41
53
  };
42
54
  teamInvites: {
43
55
  queries: {
44
- getInviteInfo: any;
56
+ getInviteInfo: import("convex/server").FunctionReference<"query", "public", any, any, string | undefined>;
45
57
  };
46
58
  };
47
59
  polar: {
48
- acceptTeamInvite: any;
60
+ acceptTeamInvite: import("convex/server").FunctionReference<"action", "public", any, any, string | undefined>;
49
61
  };
50
62
  projectSecrets: {
51
63
  queries: {
52
- getOwnSecret: any;
53
- listOwnSecretsForTeam: any;
64
+ getOwnSecret: import("convex/server").FunctionReference<"query", "public", any, any, string | undefined>;
65
+ listOwnSecretsForTeam: import("convex/server").FunctionReference<"query", "public", any, any, string | undefined>;
54
66
  };
55
67
  mutations: {
56
- ensureOwnSecret: any;
57
- revokeOwnSecret: any;
68
+ ensureOwnSecret: import("convex/server").FunctionReference<"mutation", "public", any, any, string | undefined>;
69
+ revokeOwnSecret: import("convex/server").FunctionReference<"mutation", "public", any, any, string | undefined>;
58
70
  };
59
71
  };
60
72
  lib: {
61
73
  authActions: {
62
- exchangeAuthorizationCode: any;
63
- refreshAccessToken: any;
74
+ exchangeAuthorizationCode: import("convex/server").FunctionReference<"action", "public", any, any, string | undefined>;
75
+ refreshAccessToken: import("convex/server").FunctionReference<"action", "public", any, any, string | undefined>;
64
76
  };
65
77
  };
66
78
  };
package/dist/convex.js CHANGED
@@ -1,68 +1,84 @@
1
1
  /**
2
- * Convex API stub for CLI usage
3
- * This will be replaced with actual generated types when linked to the Convex backend
2
+ * Convex API stub for CLI usage.
3
+ *
4
+ * The CLI doesn't import generated convex types (those live in the convex
5
+ * package and aren't shipped to npm consumers), so we hand-roll the `api`
6
+ * object that gets passed into `ConvexHttpClient.query/mutation/action`.
7
+ *
8
+ * `makeFunctionReference<"query" | "mutation" | "action">("path:fn")` is the
9
+ * official escape hatch for this exact case — it builds a `FunctionReference`
10
+ * with the right shape for the convex client to consume, without us having to
11
+ * cast through `any`.
12
+ *
13
+ * The args/return types default to `any` inside the convex library types
14
+ * (that's `FunctionReference`'s public surface), but at the call site we still
15
+ * get a properly typed reference object — no user-authored `any` here.
4
16
  */
17
+ import { makeFunctionReference } from "convex/server";
18
+ const query = (name) => makeFunctionReference(name);
19
+ const mutation = (name) => makeFunctionReference(name);
20
+ const action = (name) => makeFunctionReference(name);
5
21
  export const api = {
6
22
  documents: {
7
23
  queries: {
8
- exportForCli: 'documents/queries:exportForCli',
9
- exportProjectForCli: 'documents/queries:exportProjectForCli',
10
- checkDocumentsExist: 'documents/queries:checkDocumentsExist',
24
+ exportForCli: query("documents/queries:exportForCli"),
25
+ exportProjectForCli: query("documents/queries:exportProjectForCli"),
26
+ checkDocumentsExist: query("documents/queries:checkDocumentsExist"),
11
27
  // SYNC-CLI-EDIT-2: Get document metadata for conflict detection
12
- getDocumentsMetadata: 'documents/queries:getDocumentsMetadata',
28
+ getDocumentsMetadata: query("documents/queries:getDocumentsMetadata"),
13
29
  },
14
30
  mutations: {
15
- create: 'documents/mutations:create',
16
- update: 'documents/mutations:update',
31
+ create: mutation("documents/mutations:create"),
32
+ update: mutation("documents/mutations:update"),
17
33
  },
18
34
  // SYNC-ARCH-1: Save document with requirements derivation
19
35
  saveWithRequirements: {
20
- saveWithRequirements: 'documents/saveWithRequirements:saveWithRequirements',
36
+ saveWithRequirements: mutation("documents/saveWithRequirements:saveWithRequirements"),
21
37
  },
22
38
  },
23
39
  requirements: {
24
40
  queries: {
25
- listByProject: 'requirements/queries:listByProject',
41
+ listByProject: query("requirements/queries:listByProject"),
26
42
  },
27
43
  mutations: {
28
- bulkUpdate: 'requirements/mutations:bulkUpdate',
44
+ bulkUpdate: mutation("requirements/mutations:bulkUpdate"),
29
45
  },
30
46
  },
31
47
  projects: {
32
48
  mutations: {
33
- create: 'projects/mutations:create',
49
+ create: mutation("projects/mutations:create"),
34
50
  },
35
51
  },
36
52
  teams: {
37
53
  queries: {
38
- listForUserWithUsage: 'teams/queries:listForUserWithUsage',
54
+ listForUserWithUsage: query("teams/queries:listForUserWithUsage"),
39
55
  },
40
56
  mutations: {
41
- ensureDefaultTeam: 'teams/mutations:ensureDefaultTeam',
57
+ ensureDefaultTeam: mutation("teams/mutations:ensureDefaultTeam"),
42
58
  },
43
59
  },
44
60
  teamInvites: {
45
61
  queries: {
46
- getInviteInfo: 'teamInvites/queries:getInviteInfo',
62
+ getInviteInfo: query("teamInvites/queries:getInviteInfo"),
47
63
  },
48
64
  },
49
65
  polar: {
50
- acceptTeamInvite: 'polar:acceptTeamInvite',
66
+ acceptTeamInvite: action("polar:acceptTeamInvite"),
51
67
  },
52
68
  projectSecrets: {
53
69
  queries: {
54
- getOwnSecret: 'projectSecrets/queries:getOwnSecret',
55
- listOwnSecretsForTeam: 'projectSecrets/queries:listOwnSecretsForTeam',
70
+ getOwnSecret: query("projectSecrets/queries:getOwnSecret"),
71
+ listOwnSecretsForTeam: query("projectSecrets/queries:listOwnSecretsForTeam"),
56
72
  },
57
73
  mutations: {
58
- ensureOwnSecret: 'projectSecrets/mutations:ensureOwnSecret',
59
- revokeOwnSecret: 'projectSecrets/mutations:revokeOwnSecret',
74
+ ensureOwnSecret: mutation("projectSecrets/mutations:ensureOwnSecret"),
75
+ revokeOwnSecret: mutation("projectSecrets/mutations:revokeOwnSecret"),
60
76
  },
61
77
  },
62
78
  lib: {
63
79
  authActions: {
64
- exchangeAuthorizationCode: 'lib/authActions:exchangeAuthorizationCode',
65
- refreshAccessToken: 'lib/authActions:refreshAccessToken',
80
+ exchangeAuthorizationCode: action("lib/authActions:exchangeAuthorizationCode"),
81
+ refreshAccessToken: action("lib/authActions:refreshAccessToken"),
66
82
  },
67
83
  },
68
84
  };
@@ -6,7 +6,7 @@
6
6
  * - tracking.jsonl: Append-only tracking of requirement() calls (cross-process safe)
7
7
  * - coverage.json: Deduplication cache for cloud reporting
8
8
  */
9
- import { type RequirementNode } from '../schema/index.js';
9
+ import type { RequirementNode } from "../schema/index.js";
10
10
  /**
11
11
  * Tracking entry for a single requirement() call.
12
12
  * Stored as one JSON object per line in tracking.jsonl
@@ -68,10 +68,6 @@ export declare function findProjectRoot(startDir?: string): string | null;
68
68
  * Get the cache directory path, creating it if necessary
69
69
  */
70
70
  export declare function getCacheDir(requirementsDir: string, create?: boolean): string;
71
- /**
72
- * Find all *.requirements.md files under project root
73
- */
74
- export declare function findRequirementsFiles(projectRoot: string): string[];
75
71
  /**
76
72
  * Write the lookup cache with all pre-parsed requirements
77
73
  */
@@ -6,25 +6,26 @@
6
6
  * - tracking.jsonl: Append-only tracking of requirement() calls (cross-process safe)
7
7
  * - coverage.json: Deduplication cache for cloud reporting
8
8
  */
9
- import * as fs from 'fs';
10
- import * as path from 'path';
11
- import { globSync } from 'glob';
9
+ import * as fs from "node:fs";
10
+ import * as path from "node:path";
11
+ import { findRequirementsFilesSync } from "../requirements/index.js";
12
12
  // Cache directory structure
13
- const CACHE_DIR = '.cache';
14
- const LOOKUP_FILE = 'lookup.json';
15
- const TRACKING_FILE = 'tracking.jsonl';
16
- const COVERAGE_FILE = 'coverage.json';
17
- const PROJECT_ROOT_FILE = 'project-root';
13
+ const CACHE_DIR = ".cache";
14
+ const LOOKUP_FILE = "lookup.json";
15
+ const TRACKING_FILE = "tracking.jsonl";
16
+ const COVERAGE_FILE = "coverage.json";
17
+ const PROJECT_ROOT_FILE = "project-root";
18
18
  // Test run ID file (outside cache, in .requirements/)
19
- const TEST_RUN_ID_FILE = '.test-run-id';
19
+ const TEST_RUN_ID_FILE = ".test-run-id";
20
20
  /**
21
21
  * Find the nearest .requirements directory by walking up from startDir
22
22
  */
23
23
  export function findRequirementsDir(startDir = process.cwd()) {
24
24
  let currentDir = path.resolve(startDir);
25
25
  while (true) {
26
- const requirementsDir = path.join(currentDir, '.requirements');
27
- if (fs.existsSync(requirementsDir) && fs.statSync(requirementsDir).isDirectory()) {
26
+ const requirementsDir = path.join(currentDir, ".requirements");
27
+ if (fs.existsSync(requirementsDir) &&
28
+ fs.statSync(requirementsDir).isDirectory()) {
28
29
  return requirementsDir;
29
30
  }
30
31
  const parentDir = path.dirname(currentDir);
@@ -52,28 +53,6 @@ export function getCacheDir(requirementsDir, create = false) {
52
53
  }
53
54
  return cacheDir;
54
55
  }
55
- /**
56
- * Find all *.requirements.md files under project root
57
- */
58
- export function findRequirementsFiles(projectRoot) {
59
- const pattern = '**/*.requirements.md';
60
- const matches = globSync(pattern, {
61
- cwd: projectRoot,
62
- ignore: [
63
- '**/node_modules/**',
64
- '**/dist/**',
65
- '**/.git/**',
66
- '**/build/**',
67
- '**/example/**',
68
- '**/examples/**',
69
- '**/__tests__/**',
70
- '**/fixtures/**',
71
- '**/.fixtures/**',
72
- ],
73
- dot: true, // Include dotfiles/dotdirs like .requirements/
74
- });
75
- return matches.map(m => path.join(projectRoot, m));
76
- }
77
56
  /**
78
57
  * Write the lookup cache with all pre-parsed requirements
79
58
  */
@@ -101,14 +80,15 @@ export function writeLookupCache(requirementsDir, requirements) {
101
80
  const labelCounts = new Map();
102
81
  for (const child of node.children) {
103
82
  let childLabelPath = null;
104
- if (child.label && child.label !== 'requirementHeader') {
105
- const normalizedLabel = child.label.toLowerCase().replace(/\s+/g, '-');
83
+ if (child.label && child.label !== "requirementHeader") {
84
+ const normalizedLabel = child.label.toLowerCase().replace(/\s+/g, "-");
106
85
  const count = labelCounts.get(normalizedLabel) || 0;
107
86
  labelCounts.set(normalizedLabel, count + 1);
108
87
  const parentPath = labelPath || node.id;
109
- childLabelPath = count === 0
110
- ? `${parentPath}.${normalizedLabel}`
111
- : `${parentPath}.${normalizedLabel}#${count}`;
88
+ childLabelPath =
89
+ count === 0
90
+ ? `${parentPath}.${normalizedLabel}`
91
+ : `${parentPath}.${normalizedLabel}#${count}`;
112
92
  }
113
93
  flatten(child, childLabelPath);
114
94
  }
@@ -124,7 +104,7 @@ export function writeLookupCache(requirementsDir, requirements) {
124
104
  */
125
105
  export function isCacheStale(requirementsDir, cacheMtime) {
126
106
  const projectRoot = path.dirname(requirementsDir);
127
- const sourceFiles = findRequirementsFiles(projectRoot);
107
+ const sourceFiles = findRequirementsFilesSync(projectRoot);
128
108
  for (const file of sourceFiles) {
129
109
  try {
130
110
  const stat = fs.statSync(file);
@@ -159,7 +139,7 @@ export function readLookupCache(requirementsDir) {
159
139
  return null;
160
140
  }
161
141
  try {
162
- const content = fs.readFileSync(lookupPath, 'utf-8');
142
+ const content = fs.readFileSync(lookupPath, "utf-8");
163
143
  return JSON.parse(content);
164
144
  }
165
145
  catch {
@@ -183,7 +163,7 @@ export function getTestRunId(requirementsDir) {
183
163
  if (!fs.existsSync(testRunIdPath)) {
184
164
  return null;
185
165
  }
186
- return fs.readFileSync(testRunIdPath, 'utf-8').trim();
166
+ return fs.readFileSync(testRunIdPath, "utf-8").trim();
187
167
  }
188
168
  /**
189
169
  * Clean up the test run ID file
@@ -212,7 +192,7 @@ export function readProjectRoot(requirementsDir) {
212
192
  if (!fs.existsSync(projectRootPath)) {
213
193
  return null;
214
194
  }
215
- return fs.readFileSync(projectRootPath, 'utf-8').trim();
195
+ return fs.readFileSync(projectRootPath, "utf-8").trim();
216
196
  }
217
197
  /**
218
198
  * Try to find the project root from any known .requirements cache directory.
@@ -221,10 +201,10 @@ export function readProjectRoot(requirementsDir) {
221
201
  export function findCachedProjectRoot(startDir = process.cwd()) {
222
202
  let currentDir = path.resolve(startDir);
223
203
  while (true) {
224
- const requirementsDir = path.join(currentDir, '.requirements');
204
+ const requirementsDir = path.join(currentDir, ".requirements");
225
205
  const projectRootPath = path.join(requirementsDir, CACHE_DIR, PROJECT_ROOT_FILE);
226
206
  if (fs.existsSync(projectRootPath)) {
227
- return fs.readFileSync(projectRootPath, 'utf-8').trim();
207
+ return fs.readFileSync(projectRootPath, "utf-8").trim();
228
208
  }
229
209
  const parentDir = path.dirname(currentDir);
230
210
  if (parentDir === currentDir) {
@@ -249,7 +229,7 @@ export function clearTrackingFile(requirementsDir) {
249
229
  export function appendTrackingEntry(requirementsDir, entry) {
250
230
  const cacheDir = getCacheDir(requirementsDir, true);
251
231
  const trackingPath = path.join(cacheDir, TRACKING_FILE);
252
- const line = JSON.stringify(entry) + '\n';
232
+ const line = `${JSON.stringify(entry)}\n`;
253
233
  fs.appendFileSync(trackingPath, line);
254
234
  }
255
235
  /**
@@ -261,9 +241,9 @@ export function readTrackingEntries(requirementsDir) {
261
241
  if (!fs.existsSync(trackingPath)) {
262
242
  return [];
263
243
  }
264
- const content = fs.readFileSync(trackingPath, 'utf-8');
244
+ const content = fs.readFileSync(trackingPath, "utf-8");
265
245
  const entries = [];
266
- for (const line of content.split('\n')) {
246
+ for (const line of content.split("\n")) {
267
247
  if (line.trim()) {
268
248
  try {
269
249
  entries.push(JSON.parse(line));
@@ -294,20 +274,28 @@ export function deleteTrackingFile(requirementsDir) {
294
274
  * can treat both shapes uniformly.
295
275
  */
296
276
  function normalizeEntry(raw) {
297
- if (!raw || typeof raw !== 'object')
277
+ if (!raw || typeof raw !== "object")
278
+ return null;
279
+ // Cache entry shape from disk; the guards below check each field at runtime.
280
+ const entry = raw;
281
+ if (typeof entry.testRunId !== "string")
282
+ return null;
283
+ if (typeof entry.timestamp !== "number")
298
284
  return null;
299
- if (Array.isArray(raw.tuples)) {
285
+ if (Array.isArray(entry.tuples)) {
300
286
  return {
301
- testRunId: raw.testRunId,
302
- timestamp: raw.timestamp,
303
- tuples: raw.tuples,
287
+ testRunId: entry.testRunId,
288
+ timestamp: entry.timestamp,
289
+ tuples: entry.tuples,
304
290
  };
305
291
  }
306
- if (Array.isArray(raw.requirementKeys)) {
292
+ if (Array.isArray(entry.requirementKeys)) {
307
293
  return {
308
- testRunId: raw.testRunId,
309
- timestamp: raw.timestamp,
310
- tuples: raw.requirementKeys.map((requirementKey) => ({ requirementKey })),
294
+ testRunId: entry.testRunId,
295
+ timestamp: entry.timestamp,
296
+ tuples: entry.requirementKeys.map((requirementKey) => ({
297
+ requirementKey,
298
+ })),
311
299
  };
312
300
  }
313
301
  return null;
@@ -319,7 +307,7 @@ export function readCoverageCache(requirementsDir) {
319
307
  return { current: null, previous: null };
320
308
  }
321
309
  try {
322
- const content = fs.readFileSync(coveragePath, 'utf-8');
310
+ const content = fs.readFileSync(coveragePath, "utf-8");
323
311
  const raw = JSON.parse(content);
324
312
  return {
325
313
  current: normalizeEntry(raw?.current),
@@ -337,7 +325,7 @@ export function readCoverageCache(requirementsDir) {
337
325
  export function updateCoverageCache(requirementsDir, testRunId, tuples) {
338
326
  const cacheDir = getCacheDir(requirementsDir, true);
339
327
  const coveragePath = path.join(cacheDir, COVERAGE_FILE);
340
- const normalized = tuples.map((t) => typeof t === 'string' ? { requirementKey: t } : t);
328
+ const normalized = tuples.map((t) => typeof t === "string" ? { requirementKey: t } : t);
341
329
  const existing = readCoverageCache(requirementsDir);
342
330
  const newCache = {
343
331
  current: {
@@ -360,7 +348,9 @@ function tuplesMatch(a, b) {
360
348
  * Returns true if no matching tuple was reported within the debounce window.
361
349
  */
362
350
  export function needsReporting(tupleOrKey, cache) {
363
- const tuple = typeof tupleOrKey === 'string' ? { requirementKey: tupleOrKey } : tupleOrKey;
351
+ const tuple = typeof tupleOrKey === "string"
352
+ ? { requirementKey: tupleOrKey }
353
+ : tupleOrKey;
364
354
  // If no current run, everything needs reporting
365
355
  if (!cache.current) {
366
356
  return true;
@@ -1,4 +1,4 @@
1
- import { TrackedRequirement } from "./tracking.js";
1
+ import { type TrackedRequirement } from "./tracking.js";
2
2
  /**
3
3
  * Get the current git branch, or null if not in a git repo
4
4
  */