@popoverai/dotrequirements 0.23.0 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (229) hide show
  1. package/README.md +167 -20
  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-apply.d.ts +12 -0
  36. package/dist/codebase-to-spec/prompts/planner-apply.js +32 -0
  37. package/dist/codebase-to-spec/prompts/planner-initial.d.ts +11 -0
  38. package/dist/codebase-to-spec/prompts/planner-initial.js +125 -0
  39. package/dist/codebase-to-spec/prompts/planner-revise.d.ts +14 -0
  40. package/dist/codebase-to-spec/prompts/planner-revise.js +60 -0
  41. package/dist/codebase-to-spec/prompts/spec-reviewer.d.ts +16 -0
  42. package/dist/codebase-to-spec/prompts/spec-reviewer.js +96 -0
  43. package/dist/codebase-to-spec/prompts/specifier.d.ts +12 -0
  44. package/dist/codebase-to-spec/prompts/specifier.js +100 -0
  45. package/dist/codebase-to-spec/prompts/style-check.d.ts +12 -0
  46. package/dist/codebase-to-spec/prompts/style-check.js +78 -0
  47. package/dist/codebase-to-spec/schemas.d.ts +257 -0
  48. package/dist/codebase-to-spec/schemas.js +183 -0
  49. package/dist/codebase-to-spec/skill-install.d.ts +57 -0
  50. package/dist/codebase-to-spec/skill-install.js +79 -0
  51. package/dist/codebase-to-spec/slice.d.ts +49 -0
  52. package/dist/codebase-to-spec/slice.js +111 -0
  53. package/dist/codebase-to-spec/specifier.d.ts +60 -0
  54. package/dist/codebase-to-spec/specifier.js +79 -0
  55. package/dist/codebase-to-spec/style-check.d.ts +29 -0
  56. package/dist/codebase-to-spec/style-check.js +33 -0
  57. package/dist/codebase-to-spec/summary.d.ts +51 -0
  58. package/dist/codebase-to-spec/summary.js +183 -0
  59. package/dist/codebase-to-spec/validate.d.ts +46 -0
  60. package/dist/codebase-to-spec/validate.js +130 -0
  61. package/dist/commands/acceptance-test.d.ts +6 -0
  62. package/dist/commands/acceptance-test.js +212 -0
  63. package/dist/commands/ai-setup.d.ts +5 -0
  64. package/dist/commands/ai-setup.js +441 -0
  65. package/dist/commands/browsertest.js +34 -27
  66. package/dist/commands/codebase-to-spec/compose.d.ts +14 -0
  67. package/dist/commands/codebase-to-spec/compose.js +57 -0
  68. package/dist/commands/codebase-to-spec/edit-loop.d.ts +16 -0
  69. package/dist/commands/codebase-to-spec/edit-loop.js +83 -0
  70. package/dist/commands/codebase-to-spec/fan-out.d.ts +19 -0
  71. package/dist/commands/codebase-to-spec/fan-out.js +77 -0
  72. package/dist/commands/codebase-to-spec/index.d.ts +9 -0
  73. package/dist/commands/codebase-to-spec/index.js +135 -0
  74. package/dist/commands/codebase-to-spec/pack.d.ts +22 -0
  75. package/dist/commands/codebase-to-spec/pack.js +76 -0
  76. package/dist/commands/codebase-to-spec/plan-loop.d.ts +26 -0
  77. package/dist/commands/codebase-to-spec/plan-loop.js +105 -0
  78. package/dist/commands/codebase-to-spec/present.d.ts +21 -0
  79. package/dist/commands/codebase-to-spec/present.js +92 -0
  80. package/dist/commands/codebase-to-spec/run.d.ts +20 -0
  81. package/dist/commands/codebase-to-spec/run.js +85 -0
  82. package/dist/commands/codebase-to-spec/skill-install.d.ts +20 -0
  83. package/dist/commands/codebase-to-spec/skill-install.js +51 -0
  84. package/dist/commands/codebase-to-spec/specify-area.d.ts +18 -0
  85. package/dist/commands/codebase-to-spec/specify-area.js +82 -0
  86. package/dist/commands/codebase-to-spec/style-check.d.ts +15 -0
  87. package/dist/commands/codebase-to-spec/style-check.js +42 -0
  88. package/dist/commands/codebase-to-spec/validate.d.ts +18 -0
  89. package/dist/commands/codebase-to-spec/validate.js +38 -0
  90. package/dist/commands/create-requirement-document.d.ts +2 -0
  91. package/dist/commands/create-requirement-document.js +41 -0
  92. package/dist/commands/finalize.js +7 -7
  93. package/dist/commands/get.d.ts +2 -0
  94. package/dist/commands/get.js +55 -0
  95. package/dist/commands/init.js +132 -117
  96. package/dist/commands/link.js +27 -27
  97. package/dist/commands/list.d.ts +6 -0
  98. package/dist/commands/list.js +43 -0
  99. package/dist/commands/mcp-setup.js +159 -149
  100. package/dist/commands/mcp.js +1 -1
  101. package/dist/commands/prepare.js +4 -4
  102. package/dist/commands/pull.js +116 -121
  103. package/dist/commands/push.js +106 -112
  104. package/dist/commands/report.d.ts +6 -2
  105. package/dist/commands/report.js +177 -122
  106. package/dist/commands/requirements-for.d.ts +2 -0
  107. package/dist/commands/requirements-for.js +29 -0
  108. package/dist/commands/review-test.d.ts +2 -0
  109. package/dist/commands/review-test.js +75 -0
  110. package/dist/commands/search.d.ts +6 -0
  111. package/dist/commands/search.js +39 -0
  112. package/dist/commands/style-check.d.ts +7 -0
  113. package/dist/commands/style-check.js +75 -0
  114. package/dist/commands/test.js +53 -59
  115. package/dist/commands/tests-for.d.ts +2 -0
  116. package/dist/commands/tests-for.js +80 -0
  117. package/dist/commands/validate.d.ts +6 -0
  118. package/dist/commands/validate.js +72 -0
  119. package/dist/config.js +1 -1
  120. package/dist/convex.d.ts +34 -22
  121. package/dist/convex.js +38 -22
  122. package/dist/harness/cache.d.ts +1 -5
  123. package/dist/harness/cache.js +49 -59
  124. package/dist/harness/convexReporting.d.ts +1 -1
  125. package/dist/harness/convexReporting.js +9 -7
  126. package/dist/harness/coverageCache.js +3 -3
  127. package/dist/harness/finalize.js +59 -46
  128. package/dist/harness/index.d.ts +6 -7
  129. package/dist/harness/index.js +9 -10
  130. package/dist/harness/prepare.js +6 -5
  131. package/dist/harness/requirementsLoader.d.ts +2 -2
  132. package/dist/harness/requirementsLoader.js +13 -35
  133. package/dist/harness/tracking.js +18 -18
  134. package/dist/harness/types.d.ts +1 -1
  135. package/dist/mcp/convexClient.d.ts +0 -39
  136. package/dist/mcp/convexClient.js +2 -107
  137. package/dist/mcp/grep.d.ts +1 -1
  138. package/dist/mcp/grep.js +87 -42
  139. package/dist/mcp/handlers/authoring.d.ts +1 -1
  140. package/dist/mcp/handlers/authoring.js +30 -234
  141. package/dist/mcp/handlers/coverage.d.ts +1 -1
  142. package/dist/mcp/handlers/coverage.js +13 -15
  143. package/dist/mcp/handlers/debug.d.ts +2 -3
  144. package/dist/mcp/handlers/debug.js +10 -10
  145. package/dist/mcp/handlers/get.d.ts +1 -1
  146. package/dist/mcp/handlers/get.js +11 -10
  147. package/dist/mcp/handlers/index.d.ts +20 -20
  148. package/dist/mcp/handlers/index.js +10 -10
  149. package/dist/mcp/handlers/list.d.ts +4 -33
  150. package/dist/mcp/handlers/list.js +16 -38
  151. package/dist/mcp/handlers/push.d.ts +1 -1
  152. package/dist/mcp/handlers/push.js +28 -18
  153. package/dist/mcp/handlers/report.d.ts +16 -0
  154. package/dist/mcp/handlers/report.js +134 -0
  155. package/dist/mcp/handlers/review.d.ts +1 -1
  156. package/dist/mcp/handlers/review.js +40 -59
  157. package/dist/mcp/handlers/search.d.ts +1 -1
  158. package/dist/mcp/handlers/search.js +7 -9
  159. package/dist/mcp/handlers/test-mapping.d.ts +1 -1
  160. package/dist/mcp/handlers/test-mapping.js +14 -14
  161. package/dist/mcp/handlers/types.d.ts +3 -3
  162. package/dist/mcp/handlers/types.js +2 -2
  163. package/dist/mcp/index.d.ts +1 -1
  164. package/dist/mcp/index.js +147 -167
  165. package/dist/mcp/requirements.d.ts +2 -2
  166. package/dist/mcp/requirements.js +30 -30
  167. package/dist/mcp/testCodeExtractor.js +24 -26
  168. package/dist/mcp/types.d.ts +1 -1
  169. package/dist/push/core.d.ts +2 -2
  170. package/dist/push/core.js +20 -20
  171. package/dist/push/index.d.ts +1 -1
  172. package/dist/push/index.js +2 -2
  173. package/dist/requirements/cloud-ai.d.ts +57 -0
  174. package/dist/requirements/cloud-ai.js +104 -0
  175. package/dist/requirements/cloud-coverage.d.ts +41 -0
  176. package/dist/requirements/cloud-coverage.js +60 -0
  177. package/dist/requirements/coverage.d.ts +45 -0
  178. package/dist/requirements/coverage.js +114 -0
  179. package/dist/requirements/grep.d.ts +33 -0
  180. package/dist/requirements/grep.js +306 -0
  181. package/dist/requirements/index.d.ts +73 -0
  182. package/dist/requirements/index.js +174 -0
  183. package/dist/requirements/style-guide.d.ts +67 -0
  184. package/dist/requirements/style-guide.js +299 -0
  185. package/dist/requirements/testCodeExtractor.d.ts +22 -0
  186. package/dist/requirements/testCodeExtractor.js +150 -0
  187. package/dist/schema/browser.d.ts +8 -8
  188. package/dist/schema/browser.js +13 -15
  189. package/dist/schema/builder.d.ts +1 -1
  190. package/dist/schema/builder.js +13 -44
  191. package/dist/schema/conversions.d.ts +2 -2
  192. package/dist/schema/conversions.js +11 -11
  193. package/dist/schema/index.d.ts +9 -9
  194. package/dist/schema/index.js +15 -15
  195. package/dist/schema/parser-core.d.ts +1 -1
  196. package/dist/schema/parser-core.js +23 -22
  197. package/dist/schema/parser.d.ts +3 -3
  198. package/dist/schema/parser.js +27 -31
  199. package/dist/schema/resolver.d.ts +1 -1
  200. package/dist/schema/resolver.js +9 -9
  201. package/dist/schema/scenario.d.ts +1 -1
  202. package/dist/schema/scenario.js +1 -1
  203. package/dist/schema/schemas.d.ts +3 -3
  204. package/dist/schema/schemas.js +41 -28
  205. package/dist/schema/test-schema.js +27 -27
  206. package/dist/templates/context-file-section.md +3 -2
  207. package/dist/templates/example-requirements.js +1 -1
  208. package/dist/templates/example-requirements.ts +3 -1
  209. package/dist/templates/requirements-readme.js +1 -1
  210. package/dist/templates/requirements-readme.ts +1 -1
  211. package/dist/templates/skills/codebase-to-spec/SKILL.md +118 -0
  212. package/dist/utils/brand.js +3 -3
  213. package/dist/utils/browser-launch.js +4 -4
  214. package/dist/utils/context-file.d.ts +1 -1
  215. package/dist/utils/context-file.js +26 -26
  216. package/dist/utils/env.js +7 -7
  217. package/dist/utils/gitignore.js +7 -7
  218. package/dist/utils/oauth-callback-server.d.ts +1 -1
  219. package/dist/utils/oauth-callback-server.js +27 -25
  220. package/dist/utils/oauth-flow.js +32 -29
  221. package/dist/utils/project-discovery.d.ts +3 -3
  222. package/dist/utils/project-discovery.js +18 -17
  223. package/dist/utils/project-name.js +8 -8
  224. package/dist/utils/project-selector.d.ts +1 -1
  225. package/dist/utils/project-selector.js +24 -21
  226. package/dist/utils/project-settings.d.ts +1 -1
  227. package/dist/utils/project-settings.js +24 -22
  228. package/dist/utils/templates.js +6 -6
  229. package/package.json +2 -1
@@ -7,8 +7,8 @@
7
7
  * 2. DOTREQUIREMENTS_PROJECT_ROOT environment variable
8
8
  * 3. Cached project root file (.requirements/.cache/project-root) written by prepare()
9
9
  */
10
- import { type RequirementNode } from '../schema/index.js';
11
- import { Requirement } from './types.js';
10
+ import { type RequirementNode } from "../schema/index.js";
11
+ import type { Requirement } from "./types.js";
12
12
  export declare const loadedRequirements: Map<string, RequirementNode>;
13
13
  export interface LoadOptions {
14
14
  /** Explicit project root path. Takes precedence over env var and cached file. */
@@ -7,11 +7,11 @@
7
7
  * 2. DOTREQUIREMENTS_PROJECT_ROOT environment variable
8
8
  * 3. Cached project root file (.requirements/.cache/project-root) written by prepare()
9
9
  */
10
- import * as fs from 'fs';
11
- import * as path from 'path';
12
- import { globSync } from 'glob';
13
- import { parseRequirementsFromFile, resolveRequirementPath, } from '../schema/index.js';
14
- import { findCachedProjectRoot, readLookupCache, } from './cache.js';
10
+ import * as fs from "node:fs";
11
+ import * as path from "node:path";
12
+ import { findRequirementsFilesSync } from "../requirements/index.js";
13
+ import { parseRequirementsFromFile, resolveRequirementPath, } from "../schema/index.js";
14
+ import { findCachedProjectRoot, readLookupCache } from "./cache.js";
15
15
  // Cache: Maps full requirement IDs (e.g., "REQ-123.0.1") to RequirementNode
16
16
  export const loadedRequirements = new Map();
17
17
  // Cache: Maps root requirement IDs (e.g., "REQ-123") to their tree
@@ -36,34 +36,12 @@ function findProjectRoot(options = {}) {
36
36
  // Priority 3: Cached file (written by prepare())
37
37
  return findCachedProjectRoot(process.cwd());
38
38
  }
39
- /**
40
- * Find all *.requirements.md files under project root synchronously.
41
- */
42
- function findRequirementsFilesSync(projectRoot) {
43
- const pattern = '**/*.requirements.md';
44
- const matches = globSync(pattern, {
45
- cwd: projectRoot,
46
- ignore: [
47
- '**/node_modules/**',
48
- '**/dist/**',
49
- '**/.git/**',
50
- '**/build/**',
51
- '**/example/**',
52
- '**/examples/**',
53
- '**/__tests__/**',
54
- '**/fixtures/**',
55
- '**/.fixtures/**',
56
- ],
57
- dot: true, // Include dotfiles/dotdirs like .requirements/
58
- });
59
- return matches.map(m => path.join(projectRoot, m));
60
- }
61
39
  /**
62
40
  * Try to load requirements from lookup cache (fast path).
63
41
  * Returns true if cache was loaded, false if not available.
64
42
  */
65
43
  function tryLoadFromCache(projectRoot) {
66
- const requirementsDir = path.join(projectRoot, '.requirements');
44
+ const requirementsDir = path.join(projectRoot, ".requirements");
67
45
  if (!fs.existsSync(requirementsDir)) {
68
46
  return false;
69
47
  }
@@ -82,7 +60,7 @@ function tryLoadFromCache(projectRoot) {
82
60
  };
83
61
  loadedRequirements.set(id, node);
84
62
  // If this is a root node (no dot in ID), add to trees cache
85
- if (!id.includes('.')) {
63
+ if (!id.includes(".")) {
86
64
  requirementTrees.set(id, node);
87
65
  }
88
66
  }
@@ -96,7 +74,7 @@ function flattenRequirementTree(node) {
96
74
  // Add this node to the flat cache
97
75
  loadedRequirements.set(node.id, node);
98
76
  // If this is a root node (no dot in ID), also add to trees cache
99
- if (!node.id.includes('.')) {
77
+ if (!node.id.includes(".")) {
100
78
  requirementTrees.set(node.id, node);
101
79
  }
102
80
  // Recursively add children
@@ -119,8 +97,8 @@ export function loadRequirements(options = {}) {
119
97
  // Find project root
120
98
  const projectRoot = findProjectRoot(options);
121
99
  if (!projectRoot) {
122
- throw new Error('Could not find project root. ' +
123
- 'Set DOTREQUIREMENTS_PROJECT_ROOT environment variable or run prepare() in globalSetup.');
100
+ throw new Error("Could not find project root. " +
101
+ "Set DOTREQUIREMENTS_PROJECT_ROOT environment variable or run prepare() in globalSetup.");
124
102
  }
125
103
  // Try cache first (fast path)
126
104
  if (tryLoadFromCache(projectRoot)) {
@@ -128,8 +106,8 @@ export function loadRequirements(options = {}) {
128
106
  }
129
107
  // Fallback: Parse .requirements.md files on-demand
130
108
  if (!warnedAboutFallback && !options.projectRoot) {
131
- console.warn('⚠️ Performance warning: prepare() was not called. ' +
132
- 'Parsing requirements on-demand. Run prepare() in globalSetup for faster tests.');
109
+ console.warn("⚠️ Performance warning: prepare() was not called. " +
110
+ "Parsing requirements on-demand. Run prepare() in globalSetup for faster tests.");
133
111
  warnedAboutFallback = true;
134
112
  }
135
113
  // Find all .requirements.md files under the project root
@@ -144,7 +122,7 @@ export function loadRequirements(options = {}) {
144
122
  }
145
123
  }
146
124
  catch (error) {
147
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
125
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
148
126
  console.error(`Error loading ${file}:`, errorMessage);
149
127
  }
150
128
  }
@@ -6,8 +6,8 @@
6
6
  *
7
7
  * HARNESS-REQUIREMENT-3: Each requirement() call is recorded for coverage reporting
8
8
  */
9
- import * as path from 'path';
10
- import { findRequirementsDir, findProjectRoot, getTestRunId, appendTrackingEntry, getCacheDir, initTestRunId, clearTrackingFile, } from './cache.js';
9
+ import * as path from "node:path";
10
+ import { appendTrackingEntry, clearTrackingFile, findProjectRoot, findRequirementsDir, getCacheDir, getTestRunId, initTestRunId, } from "./cache.js";
11
11
  const trackedRequirements = new Map();
12
12
  // Test run tracking
13
13
  let testRunId = null;
@@ -17,7 +17,7 @@ export function ensureTestRun() {
17
17
  if (!testRunId) {
18
18
  // Priority 1: Use environment variable (cross-process persistence from globalSetup)
19
19
  if (process.env.DOTREQUIREMENTS_PROJECT_ROOT) {
20
- requirementsDirCached = path.join(process.env.DOTREQUIREMENTS_PROJECT_ROOT, '.requirements');
20
+ requirementsDirCached = path.join(process.env.DOTREQUIREMENTS_PROJECT_ROOT, ".requirements");
21
21
  }
22
22
  // Priority 2: Find from caller directory
23
23
  if (!requirementsDirCached) {
@@ -29,7 +29,7 @@ export function ensureTestRun() {
29
29
  }
30
30
  }
31
31
  if (!requirementsDirCached) {
32
- throw new Error('Could not find .requirements directory. Please create one in your project root.');
32
+ throw new Error("Could not find .requirements directory. Please create one in your project root.");
33
33
  }
34
34
  // Try to read from shared test run ID file (set by prepare())
35
35
  const existingRunId = getTestRunId(requirementsDirCached);
@@ -48,14 +48,14 @@ export function ensureTestRun() {
48
48
  // Get caller location for tracking
49
49
  export function getCallerLocation() {
50
50
  const error = new Error();
51
- const stack = error.stack?.split('\n') || [];
51
+ const stack = error.stack?.split("\n") || [];
52
52
  // Find the first stack frame that's not in our library files
53
53
  for (let i = 2; i < stack.length; i++) {
54
54
  const frame = stack[i];
55
- if (!frame.includes('/harness/index.ts') &&
56
- !frame.includes('/harness/index.js') &&
57
- !frame.includes('/harness/tracking.ts') &&
58
- !frame.includes('/harness/tracking.js')) {
55
+ if (!frame.includes("/harness/index.ts") &&
56
+ !frame.includes("/harness/index.js") &&
57
+ !frame.includes("/harness/tracking.ts") &&
58
+ !frame.includes("/harness/tracking.js")) {
59
59
  // Extract filename and line number
60
60
  const regex1 = /\((.+):(\d+):(\d+)\)/;
61
61
  const regex2 = /at (.+):(\d+):(\d+)/;
@@ -67,19 +67,19 @@ export function getCallerLocation() {
67
67
  }
68
68
  }
69
69
  }
70
- return 'unknown';
70
+ return "unknown";
71
71
  }
72
72
  // Get caller's directory (full path) for finding .requirements
73
73
  function getCallerDirectory() {
74
74
  const error = new Error();
75
- const stack = error.stack?.split('\n') || [];
75
+ const stack = error.stack?.split("\n") || [];
76
76
  // Find the first stack frame that's not in our library files
77
77
  for (let i = 2; i < stack.length; i++) {
78
78
  const frame = stack[i];
79
- if (!frame.includes('/harness/index.ts') &&
80
- !frame.includes('/harness/index.js') &&
81
- !frame.includes('/harness/tracking.ts') &&
82
- !frame.includes('/harness/tracking.js')) {
79
+ if (!frame.includes("/harness/index.ts") &&
80
+ !frame.includes("/harness/index.js") &&
81
+ !frame.includes("/harness/tracking.ts") &&
82
+ !frame.includes("/harness/tracking.js")) {
83
83
  // Extract filename
84
84
  const regex1 = /\((.+):(\d+):(\d+)\)/;
85
85
  const regex2 = /at (.+):(\d+):(\d+)/;
@@ -154,7 +154,7 @@ export function clearTracking() {
154
154
  export function initTestRun() {
155
155
  const projectRoot = findProjectRoot();
156
156
  if (!projectRoot) {
157
- throw new Error('Could not find .requirements directory. Please create one in your project root.');
157
+ throw new Error("Could not find .requirements directory. Please create one in your project root.");
158
158
  }
159
159
  const reqDir = findRequirementsDir();
160
160
  // Initialize test run ID
@@ -171,9 +171,9 @@ export function initTestRun() {
171
171
  export function finalizeTestRun() {
172
172
  // Import and call the new finalize function
173
173
  // Using dynamic import to avoid circular dependencies
174
- const { finalize } = require('./finalize.js');
174
+ const { finalize } = require("./finalize.js");
175
175
  finalize().catch((error) => {
176
- console.error('Error in finalizeTestRun:', error.message);
176
+ console.error("Error in finalizeTestRun:", error.message);
177
177
  });
178
178
  }
179
179
  //# sourceMappingURL=tracking.js.map
@@ -2,7 +2,7 @@
2
2
  * Type definitions for test harness.
3
3
  * Uses shared schema types for consistency with CLI and MCP.
4
4
  */
5
- import type { RequirementNode } from '../schema/index.js';
5
+ import type { RequirementNode } from "../schema/index.js";
6
6
  export type { RequirementNode as Requirement };
7
7
  export interface CriteriaDetail {
8
8
  reqId: string;
@@ -15,44 +15,5 @@ interface ConvexConfig {
15
15
  * Returns null if credentials are not configured.
16
16
  */
17
17
  export declare function loadConvexConfig(cwd?: string): ConvexConfig | null;
18
- /**
19
- * Query Convex for requirement coverage
20
- */
21
- export declare function getRequirementCoverage(requirementKey: string, projectId: string, projectSecret: string, convexUrl: string): Promise<{
22
- requirementKey: string;
23
- lastTestedAt: number | null;
24
- branch: string | null;
25
- testFile: string | null;
26
- testLine: number | null;
27
- allBranches: Array<{
28
- branch: string;
29
- lastTestedAt: number;
30
- testFile?: string;
31
- testLine?: number;
32
- }>;
33
- }>;
34
- /**
35
- * Query Convex for project coverage summary
36
- */
37
- export declare function getProjectCoverage(projectId: string, projectSecret: string, convexUrl: string, options?: {
38
- branch?: string;
39
- sinceTimestamp?: number;
40
- }): Promise<{
41
- tested: Array<{
42
- requirementKey: string;
43
- lastTestedAt: number;
44
- branch: string;
45
- testFile?: string;
46
- testLine?: number;
47
- }>;
48
- untested: string[];
49
- }>;
50
- /**
51
- * PROJ-CONTEXT-7: Query Convex for project context (AI style guidelines)
52
- */
53
- export declare function getProjectContext(projectId: string, projectSecret: string, convexUrl: string): Promise<{
54
- projectContext: string | null;
55
- requirementsStyleContext: string | null;
56
- } | null>;
57
18
  export {};
58
19
  //# sourceMappingURL=convexClient.d.ts.map
@@ -1,8 +1,8 @@
1
- import { getProjectCredentials } from '../utils/project-settings.js';
1
+ import { getProjectCredentials } from "../utils/project-settings.js";
2
2
  /**
3
3
  * Production Convex deployment URL
4
4
  */
5
- export const CONVEX_URL = 'https://data.dotrequirements.io';
5
+ export const CONVEX_URL = "https://data.dotrequirements.io";
6
6
  /**
7
7
  * Load Convex configuration from project settings.
8
8
  * Returns null if credentials are not configured.
@@ -21,109 +21,4 @@ export function loadConvexConfig(cwd = process.cwd()) {
21
21
  return null;
22
22
  }
23
23
  }
24
- /**
25
- * Query Convex for requirement coverage
26
- */
27
- export async function getRequirementCoverage(requirementKey, projectId, projectSecret, convexUrl) {
28
- const response = await fetch(`${convexUrl}/api/query`, {
29
- method: 'POST',
30
- headers: { 'Content-Type': 'application/json' },
31
- body: JSON.stringify({
32
- path: 'testCoverage/queries:getRequirementCoverage',
33
- args: {
34
- projectAuth: {
35
- projectSlug: projectId,
36
- projectSecret,
37
- },
38
- target: {
39
- type: 'project',
40
- slug: projectId,
41
- },
42
- requirementKey,
43
- },
44
- format: 'json',
45
- }),
46
- });
47
- if (!response.ok) {
48
- throw new Error(`Failed to query coverage: ${response.statusText}`);
49
- }
50
- const json = await response.json();
51
- if (json.status === 'error') {
52
- throw new Error(`Convex query failed: ${json.errorMessage}`);
53
- }
54
- return json.value;
55
- }
56
- /**
57
- * Query Convex for project coverage summary
58
- */
59
- export async function getProjectCoverage(projectId, projectSecret, convexUrl, options) {
60
- const response = await fetch(`${convexUrl}/api/query`, {
61
- method: 'POST',
62
- headers: { 'Content-Type': 'application/json' },
63
- body: JSON.stringify({
64
- path: 'testCoverage/queries:getProjectCoverage',
65
- args: {
66
- projectAuth: {
67
- projectSlug: projectId,
68
- projectSecret,
69
- },
70
- target: {
71
- type: 'project',
72
- slug: projectId,
73
- },
74
- branch: options?.branch,
75
- sinceTimestamp: options?.sinceTimestamp,
76
- },
77
- format: 'json',
78
- }),
79
- });
80
- if (!response.ok) {
81
- throw new Error(`Failed to query coverage: ${response.statusText}`);
82
- }
83
- const json = await response.json();
84
- if (json.status === 'error') {
85
- throw new Error(`Convex query failed: ${json.errorMessage}`);
86
- }
87
- return json.value;
88
- }
89
- /**
90
- * PROJ-CONTEXT-7: Query Convex for project context (AI style guidelines)
91
- */
92
- export async function getProjectContext(projectId, projectSecret, convexUrl) {
93
- try {
94
- const response = await fetch(`${convexUrl}/api/query`, {
95
- method: 'POST',
96
- headers: { 'Content-Type': 'application/json' },
97
- body: JSON.stringify({
98
- path: 'projects/queries:get',
99
- args: {
100
- projectAuth: {
101
- projectSlug: projectId,
102
- projectSecret,
103
- },
104
- target: {
105
- type: 'project',
106
- slug: projectId,
107
- },
108
- },
109
- format: 'json',
110
- }),
111
- });
112
- if (!response.ok) {
113
- return null;
114
- }
115
- const json = await response.json();
116
- if (json.status === 'error' || !json.value) {
117
- return null;
118
- }
119
- return {
120
- projectContext: json.value.projectContext ?? null,
121
- requirementsStyleContext: json.value.requirementsStyleContext ?? null,
122
- };
123
- }
124
- catch {
125
- // Non-fatal: return null if query fails
126
- return null;
127
- }
128
- }
129
24
  //# sourceMappingURL=convexClient.js.map
@@ -1,4 +1,4 @@
1
- import type { TestReference } from './types.js';
1
+ import type { TestReference } from "./types.js";
2
2
  /**
3
3
  * Find all test files that reference a specific requirement ID
4
4
  *
package/dist/mcp/grep.js CHANGED
@@ -1,12 +1,12 @@
1
- import { execFile } from 'child_process';
2
- import { promisify } from 'util';
1
+ import { execFile } from "node:child_process";
2
+ import { promisify } from "node:util";
3
3
  const execFileAsync = promisify(execFile);
4
4
  /**
5
5
  * Check if ripgrep is available
6
6
  */
7
7
  async function hasRipgrep() {
8
8
  try {
9
- await execFileAsync('rg', ['--version']);
9
+ await execFileAsync("rg", ["--version"]);
10
10
  return true;
11
11
  }
12
12
  catch {
@@ -21,7 +21,7 @@ async function hasRipgrep() {
21
21
  export async function findTestsForRequirement(workspaceRoot, requirementId) {
22
22
  const useRg = await hasRipgrep();
23
23
  // Escape special regex characters in the ID
24
- const escapedId = requirementId.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
24
+ const escapedId = requirementId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
25
25
  // Pattern to match requirement('ID'), requirement("ID"), or requirement(`ID`)
26
26
  const pattern = `requirement\\s*\\(\\s*['"\`]${escapedId}`;
27
27
  try {
@@ -29,12 +29,34 @@ export async function findTestsForRequirement(workspaceRoot, requirementId) {
29
29
  if (useRg) {
30
30
  // Use ripgrep for better performance
31
31
  // Using execFile with array args to avoid shell injection
32
- const { stdout } = await execFileAsync('rg', ['-n', '--column', pattern, '--type', 'ts', '--type', 'js', '--type', 'tsx', '--type', 'jsx', workspaceRoot], { maxBuffer: 10 * 1024 * 1024 }).catch(() => ({ stdout: '' })); // ripgrep returns non-zero on no matches
32
+ const { stdout } = await execFileAsync("rg", [
33
+ "-n",
34
+ "--column",
35
+ pattern,
36
+ "--type",
37
+ "ts",
38
+ "--type",
39
+ "js",
40
+ "--type",
41
+ "tsx",
42
+ "--type",
43
+ "jsx",
44
+ workspaceRoot,
45
+ ], { maxBuffer: 10 * 1024 * 1024 }).catch(() => ({ stdout: "" })); // ripgrep returns non-zero on no matches
33
46
  output = stdout;
34
47
  }
35
48
  else {
36
49
  // Fall back to grep using execFile
37
- const { stdout } = await execFileAsync('grep', ['-rn', '--include=*.ts', '--include=*.tsx', '--include=*.js', '--include=*.jsx', '-E', pattern, workspaceRoot], { maxBuffer: 10 * 1024 * 1024 }).catch(() => ({ stdout: '' })); // grep returns non-zero on no matches
50
+ const { stdout } = await execFileAsync("grep", [
51
+ "-rn",
52
+ "--include=*.ts",
53
+ "--include=*.tsx",
54
+ "--include=*.js",
55
+ "--include=*.jsx",
56
+ "-E",
57
+ pattern,
58
+ workspaceRoot,
59
+ ], { maxBuffer: 10 * 1024 * 1024 }).catch(() => ({ stdout: "" })); // grep returns non-zero on no matches
38
60
  output = stdout;
39
61
  }
40
62
  return parseGrepOutput(output, requirementId, useRg);
@@ -48,11 +70,11 @@ export async function findTestsForRequirement(workspaceRoot, requirementId) {
48
70
  * Find all requirement references in a specific file using AST parsing
49
71
  */
50
72
  export async function findRequirementsInFile(filePath) {
51
- const { parse } = await import('@babel/parser');
52
- const traverse = await import('@babel/traverse');
53
- const t = await import('@babel/types');
54
- const fs = await import('fs');
55
- const pathModule = await import('path');
73
+ const { parse } = await import("@babel/parser");
74
+ const traverse = await import("@babel/traverse");
75
+ const t = await import("@babel/types");
76
+ const fs = await import("node:fs");
77
+ const pathModule = await import("node:path");
56
78
  const results = [];
57
79
  // Resolve relative paths from current working directory
58
80
  let resolvedPath = pathModule.isAbsolute(filePath)
@@ -60,32 +82,34 @@ export async function findRequirementsInFile(filePath) {
60
82
  : pathModule.resolve(process.cwd(), filePath);
61
83
  // If file doesn't exist and path is relative, try resolving from CLI package root
62
84
  if (!pathModule.isAbsolute(filePath) && !fs.existsSync(resolvedPath)) {
63
- const cliPackageRoot = pathModule.resolve(__dirname, '../..');
85
+ const cliPackageRoot = pathModule.resolve(__dirname, "../..");
64
86
  const alternativePath = pathModule.resolve(cliPackageRoot, filePath);
65
87
  if (fs.existsSync(alternativePath)) {
66
88
  resolvedPath = alternativePath;
67
89
  }
68
90
  }
69
91
  try {
70
- const code = fs.readFileSync(resolvedPath, 'utf-8');
92
+ const code = fs.readFileSync(resolvedPath, "utf-8");
71
93
  const ast = parse(code, {
72
- sourceType: 'module',
73
- plugins: ['typescript', 'jsx'],
94
+ sourceType: "module",
95
+ plugins: ["typescript", "jsx"],
74
96
  errorRecovery: true,
75
97
  });
76
98
  // Handle both ES module and CommonJS exports for traverse
99
+ // biome-ignore lint/suspicious/noExplicitAny: @babel/traverse ships a CJS default-export that's a callable, with .default property nested 1-2 levels. The interop dance requires escaping the function-vs-namespace type wall.
77
100
  let traverseFunc = traverse;
78
- if (typeof traverseFunc !== 'function') {
101
+ if (typeof traverseFunc !== "function") {
79
102
  traverseFunc = traverseFunc.default;
80
103
  }
81
- if (typeof traverseFunc !== 'function') {
104
+ if (typeof traverseFunc !== "function") {
82
105
  traverseFunc = traverseFunc.default;
83
106
  }
84
107
  traverseFunc(ast, {
108
+ // biome-ignore lint/suspicious/noExplicitAny: babel-traverse visitor `path` is a deeply-generic NodePath whose precise type depends on what handler you're inside; `any` is the documented practice for plugin code.
85
109
  CallExpression(path) {
86
110
  const callee = path.node.callee;
87
111
  // Check if this is a requirement() call
88
- if (t.isIdentifier(callee) && callee.name === 'requirement') {
112
+ if (t.isIdentifier(callee) && callee.name === "requirement") {
89
113
  const args = path.node.arguments;
90
114
  const loc = path.node.loc;
91
115
  // Extract all string literal arguments (supports multi-requirement calls)
@@ -98,7 +122,7 @@ export async function findRequirementsInFile(filePath) {
98
122
  line: loc.start.line,
99
123
  column: loc.start.column + 1, // 1-indexed
100
124
  requirementId,
101
- context: code.split('\n')[loc.start.line - 1] || '',
125
+ context: code.split("\n")[loc.start.line - 1] || "",
102
126
  });
103
127
  }
104
128
  }
@@ -125,14 +149,36 @@ export async function findAllTestReferences(workspaceRoot) {
125
149
  let output;
126
150
  if (useRg) {
127
151
  // Using execFile with array args to avoid shell injection
128
- const { stdout } = await execFileAsync('rg', ['-n', '--column', pattern, '--type', 'ts', '--type', 'js', '--type', 'tsx', '--type', 'jsx', workspaceRoot], { maxBuffer: 10 * 1024 * 1024 }).catch(() => ({ stdout: '' })); // ripgrep returns non-zero on no matches
152
+ const { stdout } = await execFileAsync("rg", [
153
+ "-n",
154
+ "--column",
155
+ pattern,
156
+ "--type",
157
+ "ts",
158
+ "--type",
159
+ "js",
160
+ "--type",
161
+ "tsx",
162
+ "--type",
163
+ "jsx",
164
+ workspaceRoot,
165
+ ], { maxBuffer: 10 * 1024 * 1024 }).catch(() => ({ stdout: "" })); // ripgrep returns non-zero on no matches
129
166
  output = stdout;
130
167
  }
131
168
  else {
132
- const { stdout } = await execFileAsync('grep', ['-rn', '--include=*.ts', '--include=*.tsx', '--include=*.js', '--include=*.jsx', '-E', pattern, workspaceRoot], { maxBuffer: 10 * 1024 * 1024 }).catch(() => ({ stdout: '' })); // grep returns non-zero on no matches
169
+ const { stdout } = await execFileAsync("grep", [
170
+ "-rn",
171
+ "--include=*.ts",
172
+ "--include=*.tsx",
173
+ "--include=*.js",
174
+ "--include=*.jsx",
175
+ "-E",
176
+ pattern,
177
+ workspaceRoot,
178
+ ], { maxBuffer: 10 * 1024 * 1024 }).catch(() => ({ stdout: "" })); // grep returns non-zero on no matches
133
179
  output = stdout;
134
180
  }
135
- return parseGrepOutput(output, '', useRg);
181
+ return parseGrepOutput(output, "", useRg);
136
182
  }
137
183
  catch {
138
184
  return [];
@@ -143,7 +189,7 @@ export async function findAllTestReferences(workspaceRoot) {
143
189
  */
144
190
  function parseGrepOutput(output, expectedId, isRipgrep, singleFilePath) {
145
191
  const results = [];
146
- const lines = output.trim().split('\n').filter(Boolean);
192
+ const lines = output.trim().split("\n").filter(Boolean);
147
193
  for (const line of lines) {
148
194
  let match;
149
195
  let file;
@@ -200,41 +246,43 @@ function parseGrepOutput(output, expectedId, isRipgrep, singleFilePath) {
200
246
  * Uses AST parsing instead of grep for reliability across environments
201
247
  */
202
248
  export async function getReferencedRequirementIds(workspaceRoot) {
203
- const { glob } = await import('glob');
204
- const { parse } = await import('@babel/parser');
205
- const traverse = await import('@babel/traverse');
206
- const t = await import('@babel/types');
207
- const fs = await import('fs');
249
+ const { glob } = await import("glob");
250
+ const { parse } = await import("@babel/parser");
251
+ const traverse = await import("@babel/traverse");
252
+ const t = await import("@babel/types");
253
+ const fs = await import("node:fs");
208
254
  // Find all test files using glob (same pattern as get_requirement tool)
209
- const testFiles = await glob('**/*.{test,spec}.{js,jsx,ts,tsx}', {
255
+ const testFiles = await glob("**/*.{test,spec}.{js,jsx,ts,tsx}", {
210
256
  cwd: workspaceRoot,
211
257
  absolute: true,
212
- ignore: ['**/node_modules/**', '**/dist/**', '**/build/**'],
258
+ ignore: ["**/node_modules/**", "**/dist/**", "**/build/**"],
213
259
  });
214
260
  const allRequirementIds = [];
215
261
  // For each test file, parse AST and extract requirement IDs
216
262
  for (const file of testFiles) {
217
263
  try {
218
- const code = fs.readFileSync(file, 'utf-8');
264
+ const code = fs.readFileSync(file, "utf-8");
219
265
  const ast = parse(code, {
220
- sourceType: 'module',
221
- plugins: ['typescript', 'jsx'],
266
+ sourceType: "module",
267
+ plugins: ["typescript", "jsx"],
222
268
  errorRecovery: true,
223
269
  });
224
270
  // Handle both ES module and CommonJS exports for traverse
225
271
  // @babel/traverse can be imported as: traverse.default.default in compiled code
272
+ // biome-ignore lint/suspicious/noExplicitAny: see earlier comment on babel/traverse CJS/ESM interop
226
273
  let traverseFunc = traverse;
227
- if (typeof traverseFunc !== 'function') {
274
+ if (typeof traverseFunc !== "function") {
228
275
  traverseFunc = traverseFunc.default;
229
276
  }
230
- if (typeof traverseFunc !== 'function') {
277
+ if (typeof traverseFunc !== "function") {
231
278
  traverseFunc = traverseFunc.default;
232
279
  }
233
280
  traverseFunc(ast, {
281
+ // biome-ignore lint/suspicious/noExplicitAny: babel-traverse visitor `path` is a deeply-generic NodePath whose precise type depends on what handler you're inside; `any` is the documented practice for plugin code.
234
282
  CallExpression(path) {
235
283
  const callee = path.node.callee;
236
284
  // Check if this is a requirement() call
237
- if (t.isIdentifier(callee) && callee.name === 'requirement') {
285
+ if (t.isIdentifier(callee) && callee.name === "requirement") {
238
286
  const args = path.node.arguments;
239
287
  // Extract all string literal arguments (supports multi-requirement calls)
240
288
  for (const arg of args) {
@@ -246,14 +294,11 @@ export async function getReferencedRequirementIds(workspaceRoot) {
246
294
  },
247
295
  });
248
296
  }
249
- catch (error) {
250
- // Skip unparseable files
251
- continue;
252
- }
297
+ catch (error) { }
253
298
  }
254
299
  // Extract root IDs by removing the path suffix (everything after the first dot)
255
- const rootIds = allRequirementIds.map(id => {
256
- const dotIndex = id.indexOf('.');
300
+ const rootIds = allRequirementIds.map((id) => {
301
+ const dotIndex = id.indexOf(".");
257
302
  return dotIndex > 0 ? id.substring(0, dotIndex) : id;
258
303
  });
259
304
  return new Set(rootIds);
@@ -5,7 +5,7 @@
5
5
  * - create_requirement_document: Generate requirements template with format guidance
6
6
  * - validate_requirements: Validate requirements file syntax offline
7
7
  */
8
- import type { HandlerContext, ToolResponse } from './types.js';
8
+ import type { HandlerContext, ToolResponse } from "./types.js";
9
9
  /**
10
10
  * Arguments for create_requirement_document tool
11
11
  */