@popoverai/dotrequirements 0.22.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 (230) hide show
  1. package/README.md +173 -23
  2. package/dist/cli.js +121 -61
  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.d.ts +0 -1
  66. package/dist/commands/browsertest.js +51 -26
  67. package/dist/commands/codebase-to-spec/compose.d.ts +14 -0
  68. package/dist/commands/codebase-to-spec/compose.js +57 -0
  69. package/dist/commands/codebase-to-spec/edit-loop.d.ts +16 -0
  70. package/dist/commands/codebase-to-spec/edit-loop.js +83 -0
  71. package/dist/commands/codebase-to-spec/fan-out.d.ts +19 -0
  72. package/dist/commands/codebase-to-spec/fan-out.js +77 -0
  73. package/dist/commands/codebase-to-spec/index.d.ts +9 -0
  74. package/dist/commands/codebase-to-spec/index.js +135 -0
  75. package/dist/commands/codebase-to-spec/pack.d.ts +22 -0
  76. package/dist/commands/codebase-to-spec/pack.js +76 -0
  77. package/dist/commands/codebase-to-spec/plan-loop.d.ts +26 -0
  78. package/dist/commands/codebase-to-spec/plan-loop.js +105 -0
  79. package/dist/commands/codebase-to-spec/present.d.ts +21 -0
  80. package/dist/commands/codebase-to-spec/present.js +92 -0
  81. package/dist/commands/codebase-to-spec/run.d.ts +20 -0
  82. package/dist/commands/codebase-to-spec/run.js +85 -0
  83. package/dist/commands/codebase-to-spec/skill-install.d.ts +20 -0
  84. package/dist/commands/codebase-to-spec/skill-install.js +51 -0
  85. package/dist/commands/codebase-to-spec/specify-area.d.ts +18 -0
  86. package/dist/commands/codebase-to-spec/specify-area.js +82 -0
  87. package/dist/commands/codebase-to-spec/style-check.d.ts +15 -0
  88. package/dist/commands/codebase-to-spec/style-check.js +42 -0
  89. package/dist/commands/codebase-to-spec/validate.d.ts +18 -0
  90. package/dist/commands/codebase-to-spec/validate.js +38 -0
  91. package/dist/commands/create-requirement-document.d.ts +2 -0
  92. package/dist/commands/create-requirement-document.js +41 -0
  93. package/dist/commands/finalize.js +7 -7
  94. package/dist/commands/get.d.ts +2 -0
  95. package/dist/commands/get.js +55 -0
  96. package/dist/commands/init.js +132 -117
  97. package/dist/commands/link.js +27 -27
  98. package/dist/commands/list.d.ts +6 -0
  99. package/dist/commands/list.js +43 -0
  100. package/dist/commands/mcp-setup.js +159 -149
  101. package/dist/commands/mcp.js +1 -1
  102. package/dist/commands/prepare.js +4 -4
  103. package/dist/commands/pull.js +116 -121
  104. package/dist/commands/push.js +106 -112
  105. package/dist/commands/report.d.ts +6 -2
  106. package/dist/commands/report.js +177 -122
  107. package/dist/commands/requirements-for.d.ts +2 -0
  108. package/dist/commands/requirements-for.js +29 -0
  109. package/dist/commands/review-test.d.ts +2 -0
  110. package/dist/commands/review-test.js +75 -0
  111. package/dist/commands/search.d.ts +6 -0
  112. package/dist/commands/search.js +39 -0
  113. package/dist/commands/style-check.d.ts +7 -0
  114. package/dist/commands/style-check.js +75 -0
  115. package/dist/commands/test.js +53 -59
  116. package/dist/commands/tests-for.d.ts +2 -0
  117. package/dist/commands/tests-for.js +80 -0
  118. package/dist/commands/validate.d.ts +6 -0
  119. package/dist/commands/validate.js +72 -0
  120. package/dist/config.js +1 -1
  121. package/dist/convex.d.ts +34 -22
  122. package/dist/convex.js +38 -22
  123. package/dist/harness/cache.d.ts +1 -5
  124. package/dist/harness/cache.js +49 -59
  125. package/dist/harness/convexReporting.d.ts +1 -1
  126. package/dist/harness/convexReporting.js +9 -7
  127. package/dist/harness/coverageCache.js +3 -3
  128. package/dist/harness/finalize.js +59 -46
  129. package/dist/harness/index.d.ts +6 -7
  130. package/dist/harness/index.js +9 -10
  131. package/dist/harness/prepare.js +6 -5
  132. package/dist/harness/requirementsLoader.d.ts +2 -2
  133. package/dist/harness/requirementsLoader.js +13 -35
  134. package/dist/harness/tracking.js +18 -18
  135. package/dist/harness/types.d.ts +1 -1
  136. package/dist/mcp/convexClient.d.ts +0 -39
  137. package/dist/mcp/convexClient.js +2 -107
  138. package/dist/mcp/grep.d.ts +1 -1
  139. package/dist/mcp/grep.js +87 -42
  140. package/dist/mcp/handlers/authoring.d.ts +1 -1
  141. package/dist/mcp/handlers/authoring.js +30 -234
  142. package/dist/mcp/handlers/coverage.d.ts +1 -1
  143. package/dist/mcp/handlers/coverage.js +13 -15
  144. package/dist/mcp/handlers/debug.d.ts +2 -3
  145. package/dist/mcp/handlers/debug.js +10 -10
  146. package/dist/mcp/handlers/get.d.ts +1 -1
  147. package/dist/mcp/handlers/get.js +11 -10
  148. package/dist/mcp/handlers/index.d.ts +20 -20
  149. package/dist/mcp/handlers/index.js +10 -10
  150. package/dist/mcp/handlers/list.d.ts +4 -33
  151. package/dist/mcp/handlers/list.js +16 -38
  152. package/dist/mcp/handlers/push.d.ts +1 -1
  153. package/dist/mcp/handlers/push.js +28 -18
  154. package/dist/mcp/handlers/report.d.ts +16 -0
  155. package/dist/mcp/handlers/report.js +134 -0
  156. package/dist/mcp/handlers/review.d.ts +1 -1
  157. package/dist/mcp/handlers/review.js +40 -59
  158. package/dist/mcp/handlers/search.d.ts +1 -1
  159. package/dist/mcp/handlers/search.js +7 -9
  160. package/dist/mcp/handlers/test-mapping.d.ts +1 -1
  161. package/dist/mcp/handlers/test-mapping.js +14 -14
  162. package/dist/mcp/handlers/types.d.ts +3 -3
  163. package/dist/mcp/handlers/types.js +2 -2
  164. package/dist/mcp/index.d.ts +1 -1
  165. package/dist/mcp/index.js +156 -178
  166. package/dist/mcp/requirements.d.ts +2 -2
  167. package/dist/mcp/requirements.js +30 -30
  168. package/dist/mcp/testCodeExtractor.js +24 -26
  169. package/dist/mcp/types.d.ts +1 -1
  170. package/dist/push/core.d.ts +2 -2
  171. package/dist/push/core.js +20 -20
  172. package/dist/push/index.d.ts +1 -1
  173. package/dist/push/index.js +2 -2
  174. package/dist/requirements/cloud-ai.d.ts +57 -0
  175. package/dist/requirements/cloud-ai.js +104 -0
  176. package/dist/requirements/cloud-coverage.d.ts +41 -0
  177. package/dist/requirements/cloud-coverage.js +60 -0
  178. package/dist/requirements/coverage.d.ts +45 -0
  179. package/dist/requirements/coverage.js +114 -0
  180. package/dist/requirements/grep.d.ts +33 -0
  181. package/dist/requirements/grep.js +306 -0
  182. package/dist/requirements/index.d.ts +73 -0
  183. package/dist/requirements/index.js +174 -0
  184. package/dist/requirements/style-guide.d.ts +67 -0
  185. package/dist/requirements/style-guide.js +299 -0
  186. package/dist/requirements/testCodeExtractor.d.ts +22 -0
  187. package/dist/requirements/testCodeExtractor.js +150 -0
  188. package/dist/schema/browser.d.ts +8 -6
  189. package/dist/schema/browser.js +14 -14
  190. package/dist/schema/builder.d.ts +1 -1
  191. package/dist/schema/builder.js +13 -44
  192. package/dist/schema/conversions.d.ts +2 -2
  193. package/dist/schema/conversions.js +11 -11
  194. package/dist/schema/index.d.ts +9 -7
  195. package/dist/schema/index.js +15 -13
  196. package/dist/schema/parser-core.d.ts +1 -1
  197. package/dist/schema/parser-core.js +23 -22
  198. package/dist/schema/parser.d.ts +3 -3
  199. package/dist/schema/parser.js +27 -31
  200. package/dist/schema/resolver.d.ts +1 -1
  201. package/dist/schema/resolver.js +9 -9
  202. package/dist/schema/scenario.d.ts +91 -0
  203. package/dist/schema/scenario.js +82 -0
  204. package/dist/schema/schemas.d.ts +3 -3
  205. package/dist/schema/schemas.js +41 -28
  206. package/dist/schema/test-schema.js +27 -27
  207. package/dist/templates/context-file-section.md +3 -2
  208. package/dist/templates/example-requirements.js +1 -1
  209. package/dist/templates/example-requirements.ts +3 -1
  210. package/dist/templates/requirements-readme.js +1 -1
  211. package/dist/templates/requirements-readme.ts +1 -1
  212. package/dist/templates/skills/codebase-to-spec/SKILL.md +118 -0
  213. package/dist/utils/brand.js +3 -3
  214. package/dist/utils/browser-launch.js +4 -4
  215. package/dist/utils/context-file.d.ts +1 -1
  216. package/dist/utils/context-file.js +26 -26
  217. package/dist/utils/env.js +7 -7
  218. package/dist/utils/gitignore.js +7 -7
  219. package/dist/utils/oauth-callback-server.d.ts +1 -1
  220. package/dist/utils/oauth-callback-server.js +27 -25
  221. package/dist/utils/oauth-flow.js +32 -29
  222. package/dist/utils/project-discovery.d.ts +3 -3
  223. package/dist/utils/project-discovery.js +18 -17
  224. package/dist/utils/project-name.js +8 -8
  225. package/dist/utils/project-selector.d.ts +1 -1
  226. package/dist/utils/project-selector.js +24 -21
  227. package/dist/utils/project-settings.d.ts +5 -4
  228. package/dist/utils/project-settings.js +28 -20
  229. package/dist/utils/templates.js +6 -6
  230. package/package.json +2 -1
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Parse Markdown requirements files into typed structures.
3
3
  */
4
- import { RequirementNode, RequirementsFile, ParsedCriterion } from './schemas.js';
4
+ import { type ParsedCriterion, type RequirementNode, type RequirementsFile } from "./schemas.js";
5
5
  /**
6
6
  * Parse a criterion line in "position. Label → content" format.
7
7
  * Example: "0. Given → user has valid credentials"
@@ -18,14 +18,14 @@ export declare function parseRequirementBlock(key: string, blockContent: string)
18
18
  * Parse a complete requirements Markdown file.
19
19
  */
20
20
  export declare function parseRequirementsFile(markdownContent: string): {
21
- metadata: RequirementsFile['_meta'];
21
+ metadata: RequirementsFile["_meta"];
22
22
  requirements: RequirementNode[];
23
23
  };
24
24
  /**
25
25
  * Parse requirements file from disk.
26
26
  */
27
27
  export declare function parseRequirementsFromFile(filePath: string): {
28
- metadata: RequirementsFile['_meta'];
28
+ metadata: RequirementsFile["_meta"];
29
29
  requirements: RequirementNode[];
30
30
  };
31
31
  /**
@@ -1,15 +1,10 @@
1
1
  /**
2
2
  * Parse Markdown requirements files into typed structures.
3
3
  */
4
- import YAML from 'yaml';
5
- import * as fs from 'fs';
6
- import { ValidationError, validateMetadata, } from './schemas.js';
7
- /**
8
- * Default delimiter for requirements.
9
- * Can be overridden for organization-specific preferences.
10
- */
11
- const DEFAULT_DELIMITER = '→';
12
- const DELIMITER_PATTERN = '(?:→|->)'; // Non-capturing group for both Unicode and ASCII
4
+ import * as fs from "node:fs";
5
+ import YAML from "yaml";
6
+ import { ValidationError, validateMetadata, } from "./schemas.js";
7
+ const DELIMITER_PATTERN = "(?:→|->)"; // Non-capturing group for both Unicode and ASCII
13
8
  /**
14
9
  * Parse a criterion line in "position. Label → content" format.
15
10
  * Example: "0. Given → user has valid credentials"
@@ -26,7 +21,7 @@ export function parseCriterionLine(line, delimiter = DELIMITER_PATTERN) {
26
21
  }
27
22
  return {
28
23
  position: match[1],
29
- label: match[2] ? match[2].trim() : '', // Preserve original case, empty if no label
24
+ label: match[2] ? match[2].trim() : "", // Preserve original case, empty if no label
30
25
  content: match[3].trim(),
31
26
  };
32
27
  }
@@ -41,7 +36,7 @@ function parseRootLine(line, delimiter = DELIMITER_PATTERN) {
41
36
  const keyMatch = trimmed.match(/^([\w-]+):\s*(.+)$/);
42
37
  if (keyMatch) {
43
38
  return {
44
- label: '', // Keys use empty string for unlabeled requirements
39
+ label: "", // Keys use empty string for unlabeled requirements
45
40
  content: keyMatch[2].trim(),
46
41
  };
47
42
  }
@@ -51,12 +46,12 @@ function parseRootLine(line, delimiter = DELIMITER_PATTERN) {
51
46
  if (!match) {
52
47
  // If no delimiter found, treat entire line as content with empty label
53
48
  return {
54
- label: '',
49
+ label: "",
55
50
  content: trimmed,
56
51
  };
57
52
  }
58
53
  return {
59
- label: match[1] ? match[1].trim() : '',
54
+ label: match[1] ? match[1].trim() : "",
60
55
  content: match[2].trim(),
61
56
  };
62
57
  }
@@ -66,20 +61,20 @@ function parseRootLine(line, delimiter = DELIMITER_PATTERN) {
66
61
  * Subsequent lines are criteria with position paths.
67
62
  */
68
63
  export function parseRequirementBlock(key, blockContent) {
69
- const lines = blockContent.trim().split('\n');
64
+ const lines = blockContent.trim().split("\n");
70
65
  if (lines.length === 0) {
71
- throw new ValidationError('Empty requirement block', key);
66
+ throw new ValidationError("Empty requirement block", key);
72
67
  }
73
68
  // First line is the requirement content
74
69
  const rootParsed = parseRootLine(lines[0]);
75
70
  if (!rootParsed.content) {
76
- throw new ValidationError('Requirement content (first line) cannot be empty', key);
71
+ throw new ValidationError("Requirement content (first line) cannot be empty", key);
77
72
  }
78
73
  const root = {
79
74
  id: key,
80
75
  // Root requirements always use "requirementHeader" label for indexing/querying
81
76
  // The markdown format doesn't preserve root labels, but we standardize on this value
82
- label: 'requirementHeader',
77
+ label: "requirementHeader",
83
78
  content: rootParsed.content,
84
79
  children: [],
85
80
  };
@@ -88,7 +83,7 @@ export function parseRequirementBlock(key, blockContent) {
88
83
  for (let i = 1; i < lines.length; i++) {
89
84
  const line = lines[i];
90
85
  // Skip empty lines and comments
91
- if (!line.trim() || line.trim().startsWith('#')) {
86
+ if (!line.trim() || line.trim().startsWith("#")) {
92
87
  continue;
93
88
  }
94
89
  const criterion = parseCriterionLine(line);
@@ -108,8 +103,8 @@ export function parseRequirementBlock(key, blockContent) {
108
103
  function buildTreeFromPositions(root, rootKey, criteriaByPosition) {
109
104
  // Get all top-level children (single digit positions: "0", "1", "2")
110
105
  const topLevelPositions = Array.from(criteriaByPosition.keys())
111
- .filter(pos => !pos.includes('.'))
112
- .sort((a, b) => parseInt(a) - parseInt(b));
106
+ .filter((pos) => !pos.includes("."))
107
+ .sort((a, b) => parseInt(a, 10) - parseInt(b, 10));
113
108
  for (const position of topLevelPositions) {
114
109
  const criterion = criteriaByPosition.get(position);
115
110
  const childNode = {
@@ -129,9 +124,9 @@ function buildTreeFromPositions(root, rootKey, criteriaByPosition) {
129
124
  function buildChildrenFromPositions(parent, parentPosition, rootKey, criteriaByPosition) {
130
125
  // Find direct children (e.g., if parent is "1", find "1.0", "1.1", etc.)
131
126
  const childPositions = Array.from(criteriaByPosition.keys())
132
- .filter(pos => {
133
- const parts = pos.split('.');
134
- const parentParts = parentPosition.split('.');
127
+ .filter((pos) => {
128
+ const parts = pos.split(".");
129
+ const parentParts = parentPosition.split(".");
135
130
  // Must be exactly one level deeper
136
131
  if (parts.length !== parentParts.length + 1) {
137
132
  return false;
@@ -145,8 +140,8 @@ function buildChildrenFromPositions(parent, parentPosition, rootKey, criteriaByP
145
140
  return true;
146
141
  })
147
142
  .sort((a, b) => {
148
- const aLast = parseInt(a.split('.').pop());
149
- const bLast = parseInt(b.split('.').pop());
143
+ const aLast = parseInt(a.split(".").pop(), 10);
144
+ const bLast = parseInt(b.split(".").pop(), 10);
150
145
  return aLast - bLast;
151
146
  });
152
147
  for (const position of childPositions) {
@@ -171,7 +166,7 @@ function extractFrontmatter(content) {
171
166
  const frontmatterRegex = /^---\n([\s\S]*?)\n---\n([\s\S]*)$/;
172
167
  const match = content.match(frontmatterRegex);
173
168
  if (!match) {
174
- throw new ValidationError('Missing YAML frontmatter (should start with ---)');
169
+ throw new ValidationError("Missing YAML frontmatter (should start with ---)");
175
170
  }
176
171
  const [, frontmatterYaml, body] = match;
177
172
  let frontmatter;
@@ -179,7 +174,7 @@ function extractFrontmatter(content) {
179
174
  frontmatter = YAML.parse(frontmatterYaml);
180
175
  }
181
176
  catch (error) {
182
- throw new ValidationError('Failed to parse YAML frontmatter', undefined, error);
177
+ throw new ValidationError("Failed to parse YAML frontmatter", undefined, error);
183
178
  }
184
179
  return { frontmatter, body: body.trim() };
185
180
  }
@@ -194,8 +189,8 @@ function extractRequirementBlocks(body) {
194
189
  const blocks = new Map();
195
190
  // Find all ```dotrequirements code blocks
196
191
  const blockRegex = /```dotrequirements\n([\s\S]*?)```/gm;
197
- let match;
198
- while ((match = blockRegex.exec(body)) !== null) {
192
+ let match = blockRegex.exec(body);
193
+ while (match !== null) {
199
194
  const blockContent = match[1];
200
195
  // Extract key from first line of block (format: "KEY: content")
201
196
  const firstLineMatch = blockContent.match(/^([\w-]+):\s*(.+)/);
@@ -209,6 +204,7 @@ function extractRequirementBlocks(body) {
209
204
  const headingMatch = textBeforeBlock.match(/^#{2,6}\s+([^\n]+)\s*$/m);
210
205
  const title = headingMatch ? headingMatch[1].trim() : key;
211
206
  blocks.set(key, { title, blockContent: blockContent.trim() });
207
+ match = blockRegex.exec(body);
212
208
  }
213
209
  return blocks;
214
210
  }
@@ -223,7 +219,7 @@ export function parseRequirementsFile(markdownContent) {
223
219
  // Extract requirement blocks
224
220
  const blocks = extractRequirementBlocks(body);
225
221
  if (blocks.size === 0) {
226
- throw new ValidationError('No requirement blocks found in document');
222
+ throw new ValidationError("No requirement blocks found in document");
227
223
  }
228
224
  // Parse each requirement block
229
225
  const requirements = [];
@@ -246,7 +242,7 @@ export function parseRequirementsFile(markdownContent) {
246
242
  */
247
243
  export function parseRequirementsFromFile(filePath) {
248
244
  try {
249
- const content = fs.readFileSync(filePath, 'utf-8');
245
+ const content = fs.readFileSync(filePath, "utf-8");
250
246
  return parseRequirementsFile(content);
251
247
  }
252
248
  catch (error) {
@@ -2,7 +2,7 @@
2
2
  * Resolve label-based requirement paths to numeric paths.
3
3
  * Supports: REQ123.given, REQ123.when.then, REQ123.given#1
4
4
  */
5
- import { RequirementNode } from './schemas.js';
5
+ import type { RequirementNode } from "./schemas.js";
6
6
  /**
7
7
  * Parse a requirement path into segments.
8
8
  * E.g., "REQ123.given.then" → ["REQ123", "given", "then"]
@@ -8,7 +8,7 @@
8
8
  * E.g., "REQ123.given#1" → ["REQ123", "given#1"]
9
9
  */
10
10
  export function parseRequirementPath(path) {
11
- return path.split('.');
11
+ return path.split(".");
12
12
  }
13
13
  /**
14
14
  * Parse a path segment that may include disambiguation.
@@ -22,7 +22,7 @@ export function parsePathSegment(segment) {
22
22
  }
23
23
  return {
24
24
  label: match[1],
25
- index: match[2] ? parseInt(match[2]) : 0,
25
+ index: match[2] ? parseInt(match[2], 10) : 0,
26
26
  };
27
27
  }
28
28
  /**
@@ -31,7 +31,7 @@ export function parsePathSegment(segment) {
31
31
  * Examples: "Several-words" -> "several words", "AC" -> "ac"
32
32
  */
33
33
  function normalizeLabel(label) {
34
- return label.replace(/-/g, ' ').toLowerCase();
34
+ return label.replace(/-/g, " ").toLowerCase();
35
35
  }
36
36
  /**
37
37
  * Find children matching a label.
@@ -40,7 +40,7 @@ function normalizeLabel(label) {
40
40
  */
41
41
  export function findChildrenByLabel(node, label) {
42
42
  const normalizedLabel = normalizeLabel(label);
43
- return node.children.filter(child => normalizeLabel(child.label) === normalizedLabel);
43
+ return node.children.filter((child) => normalizeLabel(child.label) === normalizedLabel);
44
44
  }
45
45
  /**
46
46
  * Resolve a label-based path segment to a numeric index.
@@ -49,7 +49,7 @@ export function findChildrenByLabel(node, label) {
49
49
  export function resolvePathSegment(node, segment) {
50
50
  // Check if segment is numeric (direct index)
51
51
  if (/^\d+$/.test(segment)) {
52
- const index = parseInt(segment);
52
+ const index = parseInt(segment, 10);
53
53
  if (index < node.children.length) {
54
54
  return {
55
55
  child: node.children[index],
@@ -94,7 +94,7 @@ export function resolveRequirementPath(requirements, path) {
94
94
  }
95
95
  // First segment should be the root requirement ID
96
96
  const rootId = segments[0];
97
- let currentNode = requirements.find(r => r.id === rootId);
97
+ let currentNode = requirements.find((r) => r.id === rootId);
98
98
  if (!currentNode) {
99
99
  return null;
100
100
  }
@@ -130,7 +130,7 @@ export function resolveToNumericPath(requirements, path) {
130
130
  *
131
131
  * Returns paths like: ["REQ123", "REQ123.given", "REQ123.when", ...]
132
132
  */
133
- export function getAllLabelPaths(node, prefix = '') {
133
+ export function getAllLabelPaths(node, prefix = "") {
134
134
  const currentPath = prefix ? `${prefix}.${node.label}` : node.id;
135
135
  const paths = [currentPath];
136
136
  for (const child of node.children) {
@@ -148,7 +148,7 @@ export function checkPathAmbiguity(requirements, path) {
148
148
  return 0;
149
149
  }
150
150
  const rootId = segments[0];
151
- let currentNode = requirements.find(r => r.id === rootId);
151
+ let currentNode = requirements.find((r) => r.id === rootId);
152
152
  if (!currentNode) {
153
153
  return 0;
154
154
  }
@@ -160,7 +160,7 @@ export function checkPathAmbiguity(requirements, path) {
160
160
  const segment = segments[i];
161
161
  // Numeric segments are never ambiguous
162
162
  if (/^\d+$/.test(segment)) {
163
- const index = parseInt(segment);
163
+ const index = parseInt(segment, 10);
164
164
  if (index >= currentNode.children.length) {
165
165
  return 0; // Invalid path
166
166
  }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Scenario builder — turns a requirement tree into a browser-automation Scenario.
3
+ *
4
+ * Used by both the CLI (via `dotreq acceptance-test`) and the Convex Node action
5
+ * (`testCoverage/browserRun:run`) so the two paths produce identical scenarios
6
+ * for the same input.
7
+ *
8
+ * Naive passthrough: every requirement node with non-empty content becomes
9
+ * exactly one assertion keyed by its numeric-path id (e.g. "LOGIN-1" for the
10
+ * root, "LOGIN-1.0" for the first criterion). No arrange/act steps are
11
+ * emitted; Stagehand's hybrid agent is expected to infer setup from the
12
+ * assertion list. See BROWSER-RUN-3 in
13
+ * .requirements/testing-tab-browser-run.requirements.md.
14
+ *
15
+ * Types here are defined locally to avoid adding `@popoverai/browser-automation`
16
+ * as a runtime dependency of this package. They are structurally compatible
17
+ * with browser-automation's exported `Scenario` / `Step` types and can be
18
+ * passed directly to `runScenario`.
19
+ */
20
+ import type { RequirementNode } from "./schemas.js";
21
+ /**
22
+ * One step in a scenario. Structurally compatible with
23
+ * `@popoverai/browser-automation`'s `Step` type.
24
+ */
25
+ export interface ScenarioStep {
26
+ step: "arrange" | "act" | "assert";
27
+ description: string;
28
+ url?: string;
29
+ key?: string;
30
+ }
31
+ /**
32
+ * One entry in the `Scenario.variables` map. Structurally compatible with
33
+ * `@popoverai/browser-automation`'s `Variable` type — the `value` is what
34
+ * gets substituted into the scenario, and the optional `description` gives
35
+ * the agent extra context about what the substitution represents.
36
+ */
37
+ export interface ScenarioVariable {
38
+ value: string;
39
+ description?: string;
40
+ }
41
+ export type ScenarioVariables = Record<string, ScenarioVariable>;
42
+ /**
43
+ * A scenario passed to `runScenario`. Structurally compatible with
44
+ * `@popoverai/browser-automation`'s `Scenario` type.
45
+ */
46
+ export interface Scenario {
47
+ baseUrl: string;
48
+ steps: ScenarioStep[];
49
+ variables?: ScenarioVariables;
50
+ }
51
+ /**
52
+ * One requirement contributing an assertion to a scenario. The minimum shape
53
+ * the builder needs so callers sourcing requirements from different backends
54
+ * (Markdown files via `RequirementNode`, Convex rows, etc.) can all use it.
55
+ */
56
+ export interface ScenarioAssertionSource {
57
+ /** Full requirement key including path, e.g. "LOGIN-1" or "LOGIN-1.0.1". */
58
+ id: string;
59
+ /** Requirement text; becomes the assertion's `description`. */
60
+ content: string;
61
+ }
62
+ export interface BuildScenarioOptions {
63
+ /**
64
+ * The requirements contributing assertions, in the order they should be
65
+ * evaluated. Every entry with non-empty `content` becomes one `assert` step.
66
+ */
67
+ assertions: ScenarioAssertionSource[];
68
+ /** Target URL the scenario runs against; becomes `Scenario.baseUrl`. */
69
+ baseUrl: string;
70
+ /**
71
+ * Optional Stagehand variable substitutions. Used to inject secrets
72
+ * (test-user credentials, bypass tokens) into assertion descriptions
73
+ * without exposing them to the language model in plaintext.
74
+ */
75
+ variables?: Record<string, string>;
76
+ }
77
+ /**
78
+ * Build a `Scenario` from a list of requirement assertion sources.
79
+ *
80
+ * Throws if the resulting scenario would have zero assertions
81
+ * (browser-automation rejects scenarios with no asserts).
82
+ */
83
+ export declare function buildScenarioFromRequirements(opts: BuildScenarioOptions): Scenario;
84
+ /**
85
+ * Convenience wrapper for callers that have a `RequirementNode` tree (root
86
+ * plus nested children). Flattens the tree to the order produced by
87
+ * `flattenRequirementTree` (pre-order: root first, then each subtree), then
88
+ * delegates to `buildScenarioFromRequirements`.
89
+ */
90
+ export declare function requirementTreeToScenario(root: RequirementNode, opts: Omit<BuildScenarioOptions, "assertions">): Scenario;
91
+ //# sourceMappingURL=scenario.d.ts.map
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Scenario builder — turns a requirement tree into a browser-automation Scenario.
3
+ *
4
+ * Used by both the CLI (via `dotreq acceptance-test`) and the Convex Node action
5
+ * (`testCoverage/browserRun:run`) so the two paths produce identical scenarios
6
+ * for the same input.
7
+ *
8
+ * Naive passthrough: every requirement node with non-empty content becomes
9
+ * exactly one assertion keyed by its numeric-path id (e.g. "LOGIN-1" for the
10
+ * root, "LOGIN-1.0" for the first criterion). No arrange/act steps are
11
+ * emitted; Stagehand's hybrid agent is expected to infer setup from the
12
+ * assertion list. See BROWSER-RUN-3 in
13
+ * .requirements/testing-tab-browser-run.requirements.md.
14
+ *
15
+ * Types here are defined locally to avoid adding `@popoverai/browser-automation`
16
+ * as a runtime dependency of this package. They are structurally compatible
17
+ * with browser-automation's exported `Scenario` / `Step` types and can be
18
+ * passed directly to `runScenario`.
19
+ */
20
+ /**
21
+ * Pre-order flatten: root first, then each subtree's nodes in order.
22
+ * Inlined here (rather than imported from `parser.ts`) so this module stays
23
+ * free of Node-only dependencies and can be re-exported from the browser
24
+ * entry point.
25
+ */
26
+ function flattenTree(node) {
27
+ const result = [node];
28
+ for (const child of node.children) {
29
+ result.push(...flattenTree(child));
30
+ }
31
+ return result;
32
+ }
33
+ /**
34
+ * Build a `Scenario` from a list of requirement assertion sources.
35
+ *
36
+ * Throws if the resulting scenario would have zero assertions
37
+ * (browser-automation rejects scenarios with no asserts).
38
+ */
39
+ export function buildScenarioFromRequirements(opts) {
40
+ const { assertions, baseUrl, variables } = opts;
41
+ if (!baseUrl || baseUrl.trim().length === 0) {
42
+ throw new Error("buildScenarioFromRequirements: baseUrl is required");
43
+ }
44
+ const steps = [];
45
+ for (const source of assertions) {
46
+ const description = source.content.trim();
47
+ if (description.length === 0)
48
+ continue;
49
+ steps.push({
50
+ step: "assert",
51
+ description,
52
+ key: source.id,
53
+ });
54
+ }
55
+ if (steps.length === 0) {
56
+ throw new Error("buildScenarioFromRequirements: requirement tree has no nodes with content; cannot build a scenario");
57
+ }
58
+ const scenario = { baseUrl, steps };
59
+ if (variables && Object.keys(variables).length > 0) {
60
+ // Callers pass a flat Record<string, string> for ergonomics; wrap each
61
+ // entry as `{ value }` so the scenario matches runScenario's expected
62
+ // Variables shape. Callers with richer values can set variables on the
63
+ // returned scenario directly before invocation.
64
+ scenario.variables = Object.fromEntries(Object.entries(variables).map(([key, value]) => [key, { value }]));
65
+ }
66
+ return scenario;
67
+ }
68
+ /**
69
+ * Convenience wrapper for callers that have a `RequirementNode` tree (root
70
+ * plus nested children). Flattens the tree to the order produced by
71
+ * `flattenRequirementTree` (pre-order: root first, then each subtree), then
72
+ * delegates to `buildScenarioFromRequirements`.
73
+ */
74
+ export function requirementTreeToScenario(root, opts) {
75
+ const flat = flattenTree(root);
76
+ const assertions = flat.map((node) => ({
77
+ id: node.id,
78
+ content: node.content,
79
+ }));
80
+ return buildScenarioFromRequirements({ ...opts, assertions });
81
+ }
82
+ //# sourceMappingURL=scenario.js.map
@@ -2,7 +2,7 @@
2
2
  * Zod schemas for Markdown requirements format.
3
3
  * TypeScript types are inferred from these schemas using z.infer<>.
4
4
  */
5
- import { z } from 'zod';
5
+ import { z } from "zod";
6
6
  /**
7
7
  * Requirement prefix format: uppercase letters and hyphens, 1-30 chars.
8
8
  * First char must be a letter.
@@ -100,7 +100,7 @@ export declare const CONVEX_ID_PATTERN: RegExp;
100
100
  */
101
101
  export type PushValidationResult = {
102
102
  valid: true;
103
- action: 'create' | 'update';
103
+ action: "create" | "update";
104
104
  warning?: string;
105
105
  } | {
106
106
  valid: false;
@@ -246,7 +246,7 @@ export declare const RequirementsFileSchema: z.ZodObject<{
246
246
  }>;
247
247
  }, z.ZodTypeAny, "passthrough">>;
248
248
  export type RequirementsFile = z.infer<typeof RequirementsFileSchema> & {
249
- [key: string]: any;
249
+ [key: string]: unknown;
250
250
  };
251
251
  /**
252
252
  * Parsed criterion from Markdown (position. Label: content format)