@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
@@ -0,0 +1,174 @@
1
+ import * as path from "node:path";
2
+ import { glob, globSync } from "glob";
3
+ import { buildRequirementMarkdown, getAllRequirements, parseRequirementsFile, parseRequirementsFromFile, } from "../schema/index.js";
4
+ /**
5
+ * Glob pattern matching *.requirements.md files.
6
+ */
7
+ const REQUIREMENTS_FILE_PATTERN = "**/*.requirements.md";
8
+ /**
9
+ * Directories ignored when discovering requirements files.
10
+ * Shared between async and sync discovery so the two never drift.
11
+ */
12
+ const REQUIREMENTS_IGNORE_PATTERNS = [
13
+ "**/node_modules/**",
14
+ "**/dist/**",
15
+ "**/.git/**",
16
+ "**/build/**",
17
+ // Exclude example and test fixture directories
18
+ "**/example/**",
19
+ "**/examples/**",
20
+ "**/__tests__/**",
21
+ "**/fixtures/**",
22
+ "**/.fixtures/**",
23
+ ];
24
+ /**
25
+ * Find all *.requirements.md files in the workspace.
26
+ * Supports both .requirements/ directories and colocated files.
27
+ * Excludes example files, test fixtures, and build artifacts.
28
+ */
29
+ export async function findRequirementsFiles(workspaceRoot) {
30
+ const matches = await glob(REQUIREMENTS_FILE_PATTERN, {
31
+ cwd: workspaceRoot,
32
+ ignore: REQUIREMENTS_IGNORE_PATTERNS,
33
+ dot: true, // Include dotfiles/dotdirs like .requirements/
34
+ });
35
+ return matches.map((m) => path.join(workspaceRoot, m));
36
+ }
37
+ /**
38
+ * Synchronous variant of {@link findRequirementsFiles}. Used by code paths
39
+ * that can't await (e.g. the test-harness lookup-cache builder).
40
+ */
41
+ export function findRequirementsFilesSync(workspaceRoot) {
42
+ const matches = globSync(REQUIREMENTS_FILE_PATTERN, {
43
+ cwd: workspaceRoot,
44
+ ignore: REQUIREMENTS_IGNORE_PATTERNS,
45
+ dot: true,
46
+ });
47
+ return matches.map((m) => path.join(workspaceRoot, m));
48
+ }
49
+ /**
50
+ * Load all requirements from the workspace
51
+ */
52
+ export async function loadAllRequirements(workspaceRoot) {
53
+ const filePaths = await findRequirementsFiles(workspaceRoot);
54
+ const allFiles = [];
55
+ const allFlattened = [];
56
+ for (const filePath of filePaths) {
57
+ try {
58
+ const parsed = parseRequirementsFromFile(filePath);
59
+ allFiles.push(parsed);
60
+ const flattened = flattenRequirementsFile(parsed, filePath);
61
+ allFlattened.push(...flattened);
62
+ }
63
+ catch (error) {
64
+ // Skip invalid files
65
+ console.error(`Failed to parse ${filePath}:`, error);
66
+ }
67
+ }
68
+ return { files: allFiles, flattened: allFlattened };
69
+ }
70
+ /**
71
+ * Flatten a requirements file into searchable requirements
72
+ */
73
+ export function flattenRequirementsFile(file, sourceFile) {
74
+ const results = [];
75
+ // Use shared utility to get all requirements (flattened)
76
+ const allReqs = getAllRequirements(file.requirements);
77
+ for (const req of allReqs) {
78
+ // Extract position from ID (e.g., "REQ123.0.1" -> "0.1")
79
+ const idParts = req.id.split(".");
80
+ const rootId = idParts[0];
81
+ results.push({
82
+ id: req.id,
83
+ rootId: rootId,
84
+ label: req.label,
85
+ content: req.content,
86
+ path: idParts.slice(1), // ["0", "1"] for "REQ123.0.1"
87
+ sourceFile: sourceFile,
88
+ documentTitle: file.metadata.document?.title || rootId,
89
+ });
90
+ }
91
+ return results;
92
+ }
93
+ /**
94
+ * Search requirements by text query
95
+ */
96
+ export function searchRequirements(requirements, query) {
97
+ const lowerQuery = query.toLowerCase();
98
+ return requirements.filter((req) => {
99
+ // Search in ID
100
+ if (req.id.toLowerCase().includes(lowerQuery))
101
+ return true;
102
+ // Search in content
103
+ if (req.content.toLowerCase().includes(lowerQuery))
104
+ return true;
105
+ // Search in label
106
+ if (req.label.toLowerCase().includes(lowerQuery))
107
+ return true;
108
+ return false;
109
+ });
110
+ }
111
+ /**
112
+ * Get a specific requirement by ID (supports partial matching)
113
+ */
114
+ export function getRequirementById(requirements, id) {
115
+ // Exact match first
116
+ const exact = requirements.find((r) => r.id === id);
117
+ if (exact)
118
+ return exact;
119
+ // Try partial match (just the root ID)
120
+ return requirements.find((r) => r.rootId === id || r.id.startsWith(`${id}.`));
121
+ }
122
+ /**
123
+ * Get a requirement and all its children
124
+ */
125
+ export function getRequirementTree(requirements, rootId) {
126
+ return requirements.filter((r) => r.rootId === rootId || r.id === rootId);
127
+ }
128
+ /**
129
+ * Format a requirement for display
130
+ */
131
+ export function formatRequirement(req) {
132
+ const indent = " ".repeat(req.path.length);
133
+ const label = req.label ? ` (${req.label})` : "";
134
+ return `${indent}${req.id}${label}: ${req.content}`;
135
+ }
136
+ /**
137
+ * Filter a requirements file's content to only include specified requirement keys.
138
+ * Parses the file content, filters top-level requirements by key, and rebuilds as
139
+ * markdown requirement blocks (without frontmatter).
140
+ */
141
+ export function filterRequirementsByKeys(fileContents, keys) {
142
+ const { requirements } = parseRequirementsFile(fileContents);
143
+ const upperKeys = keys.map((k) => k.toUpperCase());
144
+ const filtered = requirements.filter((r) => upperKeys.includes(r.id.toUpperCase()));
145
+ const foundKeys = filtered.map((r) => r.id);
146
+ const missingKeys = upperKeys.filter((k) => !foundKeys.map((f) => f.toUpperCase()).includes(k));
147
+ const filteredContent = filtered.map(buildRequirementMarkdown).join("\n");
148
+ return { filteredContent, foundKeys, missingKeys };
149
+ }
150
+ /**
151
+ * Format a requirement tree for display
152
+ */
153
+ export function formatRequirementTree(requirements) {
154
+ // Sort by path to maintain hierarchy - children appear immediately after parent
155
+ const sorted = [...requirements].sort((a, b) => {
156
+ // Compare paths element by element for proper tree order
157
+ const maxLen = Math.max(a.path.length, b.path.length);
158
+ for (let i = 0; i < maxLen; i++) {
159
+ const aVal = i < a.path.length ? parseInt(a.path[i], 10) : -1;
160
+ const bVal = i < b.path.length ? parseInt(b.path[i], 10) : -1;
161
+ // If one path is a prefix of the other, the shorter one comes first
162
+ if (aVal === -1)
163
+ return -1;
164
+ if (bVal === -1)
165
+ return 1;
166
+ if (aVal !== bVal) {
167
+ return aVal - bVal;
168
+ }
169
+ }
170
+ return 0;
171
+ });
172
+ return sorted.map(formatRequirement).join("\n");
173
+ }
174
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Style-guide template generator. Produces the markdown document that both
3
+ * the MCP `create_requirement_document` tool and the CLI
4
+ * `dotreq create-requirement-document` verb return.
5
+ *
6
+ * The output is a full ready-to-paste-into-a-PR markdown document that
7
+ * describes the requirements format, embeds a worked example, and includes
8
+ * style principles for writing requirements and tests. Discovered label /
9
+ * key patterns from the calling workspace and (optional) project-owner-
10
+ * supplied custom guidance are interpolated into the body.
11
+ *
12
+ * If the project has a `.requirements/STYLE.md`, callers can pass its
13
+ * contents via `localStyleGuide` to use that body in place of the bundled
14
+ * defaults. See {@link readLocalStyleGuide} for the lookup helper.
15
+ */
16
+ import type { FlattenedRequirement } from "./index.js";
17
+ /**
18
+ * Conventional location of a project's STYLE.md, relative to the workspace
19
+ * root. Exported so callers (init, push/pull, tests) can reference one place.
20
+ */
21
+ export declare const STYLE_MD_PATH = ".requirements/STYLE.md";
22
+ export interface GenerateStyleGuideParams {
23
+ /** Flattened view of the workspace's existing requirements, used to
24
+ * surface label patterns and key-prefix patterns in the output. Pass
25
+ * an empty array when no requirements exist yet. */
26
+ requirements: FlattenedRequirement[];
27
+ /** Project-owner-supplied custom guidance (currently sourced from the
28
+ * cloud project's `requirementsStyleContext` field). Pass null/undefined
29
+ * to omit. */
30
+ customStyleGuidance?: string | null;
31
+ /** Suggested target path for the new file. Used only in the preamble
32
+ * and "Next Steps" section. Defaults to a generic example path. */
33
+ filePath?: string;
34
+ /** Project-local STYLE.md contents. When provided and non-empty, this
35
+ * body replaces the bundled default; callers can fetch it via
36
+ * {@link readLocalStyleGuide}. */
37
+ localStyleGuide?: string | null;
38
+ }
39
+ /**
40
+ * Read `.requirements/STYLE.md` if the workspace has one. Returns the file
41
+ * contents on success, `null` if the file is absent. Empty / whitespace-only
42
+ * files are treated as absent so the bundled defaults still apply.
43
+ */
44
+ export declare function readLocalStyleGuide(workspaceRoot: string): string | null;
45
+ /**
46
+ * Build the body of the bundled default style guide. This is the content
47
+ * that lives inside the "# Requirements File Template" preamble — the
48
+ * format syntax, key/label conventions, style principles, and example
49
+ * requirements — with discovered patterns and custom guidance interpolated.
50
+ *
51
+ * Exposed separately from {@link generateStyleGuide} so callers can:
52
+ * - scaffold `.requirements/STYLE.md` with the bundled defaults at init time
53
+ * - swap the body wholesale (via `localStyleGuide`) without losing the
54
+ * wrapping preamble + "Next Steps" footer
55
+ */
56
+ export declare function generateStyleGuideBody(params: Omit<GenerateStyleGuideParams, "filePath" | "localStyleGuide">): string;
57
+ /**
58
+ * Build a complete style-guide document for the calling workspace.
59
+ * Returns a single markdown string suitable for printing to stdout or
60
+ * wrapping in an MCP text response.
61
+ *
62
+ * When `localStyleGuide` is provided and non-empty, that content replaces
63
+ * the bundled default body. The preamble and "Next Steps" footer are
64
+ * always applied.
65
+ */
66
+ export declare function generateStyleGuide(params: GenerateStyleGuideParams): string;
67
+ //# sourceMappingURL=style-guide.d.ts.map
@@ -0,0 +1,299 @@
1
+ /**
2
+ * Style-guide template generator. Produces the markdown document that both
3
+ * the MCP `create_requirement_document` tool and the CLI
4
+ * `dotreq create-requirement-document` verb return.
5
+ *
6
+ * The output is a full ready-to-paste-into-a-PR markdown document that
7
+ * describes the requirements format, embeds a worked example, and includes
8
+ * style principles for writing requirements and tests. Discovered label /
9
+ * key patterns from the calling workspace and (optional) project-owner-
10
+ * supplied custom guidance are interpolated into the body.
11
+ *
12
+ * If the project has a `.requirements/STYLE.md`, callers can pass its
13
+ * contents via `localStyleGuide` to use that body in place of the bundled
14
+ * defaults. See {@link readLocalStyleGuide} for the lookup helper.
15
+ */
16
+ import { existsSync, readFileSync } from "node:fs";
17
+ import { join } from "node:path";
18
+ /**
19
+ * Conventional location of a project's STYLE.md, relative to the workspace
20
+ * root. Exported so callers (init, push/pull, tests) can reference one place.
21
+ */
22
+ export const STYLE_MD_PATH = ".requirements/STYLE.md";
23
+ /**
24
+ * Read `.requirements/STYLE.md` if the workspace has one. Returns the file
25
+ * contents on success, `null` if the file is absent. Empty / whitespace-only
26
+ * files are treated as absent so the bundled defaults still apply.
27
+ */
28
+ export function readLocalStyleGuide(workspaceRoot) {
29
+ const fullPath = join(workspaceRoot, STYLE_MD_PATH);
30
+ if (!existsSync(fullPath)) {
31
+ return null;
32
+ }
33
+ const contents = readFileSync(fullPath, "utf-8");
34
+ return contents.trim().length > 0 ? contents : null;
35
+ }
36
+ /**
37
+ * Build the body of the bundled default style guide. This is the content
38
+ * that lives inside the "# Requirements File Template" preamble — the
39
+ * format syntax, key/label conventions, style principles, and example
40
+ * requirements — with discovered patterns and custom guidance interpolated.
41
+ *
42
+ * Exposed separately from {@link generateStyleGuide} so callers can:
43
+ * - scaffold `.requirements/STYLE.md` with the bundled defaults at init time
44
+ * - swap the body wholesale (via `localStyleGuide`) without losing the
45
+ * wrapping preamble + "Next Steps" footer
46
+ */
47
+ export function generateStyleGuideBody(params) {
48
+ const { requirements, customStyleGuidance } = params;
49
+ const labels = new Set();
50
+ const keyPrefixes = new Set();
51
+ for (const req of requirements) {
52
+ if (req.label?.trim()) {
53
+ labels.add(req.label);
54
+ }
55
+ if (req.path.length === 0 && req.id) {
56
+ const lastDashIndex = req.id.lastIndexOf("-");
57
+ if (lastDashIndex > 0) {
58
+ keyPrefixes.add(req.id.substring(0, lastDashIndex));
59
+ }
60
+ }
61
+ }
62
+ const discoveredLabels = Array.from(labels).sort();
63
+ const discoveredPrefixes = Array.from(keyPrefixes).sort();
64
+ let labelGuidance;
65
+ if (discoveredLabels.length > 0) {
66
+ const labelList = discoveredLabels
67
+ .slice(0, 10)
68
+ .map((l) => `"${l}"`)
69
+ .join(", ");
70
+ const more = discoveredLabels.length > 10
71
+ ? ` (and ${discoveredLabels.length - 10} more)`
72
+ : "";
73
+ labelGuidance = `**Existing labels in this codebase:** ${labelList}${more}
74
+
75
+ **Use these existing labels** to maintain consistency. If you're unsure which labels to use for a new requirement, ask the user.`;
76
+ }
77
+ else {
78
+ labelGuidance = `**No existing requirements found in this codebase.**
79
+
80
+ **Default to unlabeled requirements** (\`0. → content\`). If the user wants labels, ask them which format they prefer. Do not choose an opinionated framework like Given/When/Then without explicit user consent.`;
81
+ }
82
+ let keyGuidance;
83
+ if (discoveredPrefixes.length > 0) {
84
+ const prefixList = discoveredPrefixes.map((p) => `"${p}"`).join(", ");
85
+ keyGuidance = `**Existing requirement key prefixes in this codebase:** ${prefixList}
86
+
87
+ **Match the existing pattern** when creating new requirement keys. Use the same domain prefixes and sequential numbering style.`;
88
+ }
89
+ else {
90
+ keyGuidance = `**No existing requirements found in this codebase.**
91
+
92
+ **Use concise domain prefixes** like \`AUTH-1\`, \`LOGIN-1\`, etc. Start numbering at 1 and increment sequentially.`;
93
+ }
94
+ let userStyleGuidance = "";
95
+ if (customStyleGuidance?.trim()) {
96
+ userStyleGuidance = `
97
+
98
+ ## User-Provided Style Guidelines
99
+
100
+ The following style guidelines were provided by the project owner. When these conflict with the defaults above, prioritize the user's guidelines.
101
+
102
+ ${customStyleGuidance.trim()}`;
103
+ }
104
+ const template = `---
105
+ document:
106
+ title: "Example Requirements"
107
+ ---
108
+
109
+ # Example Requirements
110
+
111
+ This template demonstrates the dotrequirements Markdown format and style guidelines.
112
+
113
+ ## Syntax Overview
114
+
115
+ **File naming:** Use \`*.requirements.md\` pattern (colocated: \`auth.requirements.md\` or centralized: \`.requirements/auth.requirements.md\`)
116
+
117
+ **Block format:**
118
+ \`\`\`dotrequirements
119
+ KEY: Root requirement content
120
+ 0. → First criterion (unlabeled)
121
+ 1. Label → Second criterion (with label)
122
+ 1.0. → Nested criterion (unlabeled)
123
+ \`\`\`
124
+
125
+ - First line: \`KEY: content\` (requirement key and description)
126
+ - Criteria: \`position. Label → content\` or \`position. → content\` (unlabeled)
127
+ - Position: \`0\`, \`1\`, \`2\` (top-level) or \`0.0\`, \`1.0\` (nested) - defines hierarchy
128
+ - Delimiter: \`→\` or \`->\` separates optional label from content
129
+
130
+ ## Requirement Keys: Concise and Sequential
131
+
132
+ ${keyGuidance}
133
+
134
+ **Key format:** \`DOMAIN-FEATURE-N\` where N is sequential (1, 2, 3...)
135
+
136
+ **Best practices:**
137
+ 1. **Concise domains** - Use short, clear prefixes
138
+ - ✅ \`AUTHZ-1\` (authorization)
139
+ - ✅ \`AUTH-1\` (authentication)
140
+ - ❌ \`AUTHORIZATION-1\` (too verbose)
141
+ - ❌ \`REQ-IDENTITY-ACCESS-AUTHZ-1\` (too nested)
142
+
143
+ 2. **Sequential, 1-indexed numbering** - Start at 1, no padding
144
+ - ✅ \`LOGIN-1\`, \`LOGIN-2\`, \`LOGIN-3\`
145
+ - ❌ \`LOGIN-0\` (don't use 0-indexing for requirement IDs)
146
+ - ❌ \`LOGIN-001\` (no zero-padding)
147
+
148
+ 3. **Unique across project** - Each key must be unique in the entire project
149
+
150
+ 4. **Match existing patterns** - Check existing requirements first and follow their convention
151
+
152
+ ## Labels: Match Your Codebase or Ask the User
153
+
154
+ ${labelGuidance}
155
+
156
+ ## Style Principles for Requirements
157
+
158
+ **1. Use Concrete Examples**: Replace vague language with specific, testable conditions.
159
+ - ❌ "users can log in" or "works properly"
160
+ - ✅ "When a registered user provides valid credentials, they are authenticated"
161
+
162
+ **2. Write Natural, Concise Prose**: Avoid terseness and verbosity. Use declarative style (not "should").
163
+ - ❌ "registered user with valid credentials is authenticated" (too terse)
164
+ - ❌ "A registered user, whose account was created on Tuesday and whose life story is as follows..." (too verbose)
165
+ - ✅ "When a registered user provides valid credentials, they are authenticated"
166
+
167
+ **3. Keep Arrange/Act/Assert in Mind**: Well-written requirements describe preconditions, trigger, and result.
168
+ - ✅ "When [preconditions:] a registered user [trigger:] provides valid credentials, [result:] they are authenticated"
169
+
170
+ **4. Be Framework Neutral**: Don't prescribe Given/When/Then vs AC vs other formats - focus on content quality.
171
+
172
+ **5. Use Named Personas**: Establish personas in parent requirements, reuse in children.
173
+ - ✅ Parent: "A registered user, Jamie, can log in normally" → Child: "When Jamie provides valid credentials, they are authenticated"
174
+
175
+ **6. Use User-Centric Language**: Describe user experience, not technical internals.
176
+ - ❌ "they are redirected to app.dotrequirements.io/redirect/dashboard"
177
+ - ✅ "they are automatically brought to the dashboard"
178
+
179
+ **7. Single Action Per Requirement**: Don't chain multiple actions with "and then".
180
+ - ❌ "When Robin provides credentials, requests an OTP, then provides the OTP..."
181
+ - ✅ Break into separate requirements for each action
182
+
183
+ **8. Each Requirement Should Be Independent**: Requirements within a block should be independently testable. If they share preconditions or form a sequence, either nest them or restate context.
184
+ - ❌ "0. → When Jordan submits signup, an account is created" / "1. → Welcome email is sent" / "2. → Dashboard appears"
185
+ - ✅ Option A: Restate context - "1. → When Jordan submits signup, a welcome email is sent"
186
+ - ✅ Option B: Use nesting - "0. When → Jordan submits signup" / " 0.0. Then → an account is created"
187
+ - Test: Can you understand what's being tested by reading just one requirement, or do you need to read its siblings?
188
+
189
+ **9. Focus on Behavior, Not Design**: Describe what happens, not UI specifics.
190
+ - ❌ "enters valid credentials into two single-line input fields and presses a green button"
191
+ - ✅ "provides valid credentials"
192
+
193
+ **10. Focus on Outcomes, Not Implementation**: User perspective, even for technical requirements.
194
+ - ❌ "When the app requests that Twilio send Casey an OTP from /email/POST endpoint..."
195
+ - ✅ "When Casey requests an email OTP..."
196
+ - Note: Even technical requirements can be user-centric: "95 percent of users experience under 1 second of delay"
197
+
198
+ **11. Decompose Large Requirements**: If it can't be validated with a single test, break it down.
199
+ ${userStyleGuidance}
200
+
201
+ ## Example Requirements
202
+
203
+ **Unlabeled (recommended default):**
204
+ \`\`\`dotrequirements
205
+ AUTH-LOGIN-1: A registered user, Jamie, can log in to their account
206
+ 0. → When Jamie provides their registered email and correct password, they are authenticated and brought to their dashboard
207
+ 1. → When Jamie provides an incorrect password, they see an error message and remain on the login page
208
+ 2. → When Jamie's account has been deactivated, they see a message explaining their account status
209
+ \`\`\`
210
+
211
+ **With labels (example only - DO NOT use opinionated formats like Given/When/Then without asking the user first):**
212
+ \`\`\`dotrequirements
213
+ PAYMENT-REFUND-1: A customer, Alex, receives a refund after returning an item
214
+ 0. Given → Alex purchased a laptop from the store 10 days ago
215
+ 1. Given → Alex initiates a return through their order history
216
+ 2. When → Alex's returned laptop is received and inspected at the warehouse
217
+ 3. Then → Alex receives a refund to their original payment method within 5 business days
218
+ 4. Then → Alex receives an email confirmation with the refund amount and expected timeline
219
+ \`\`\`
220
+
221
+ ## Referencing Requirements in Tests
222
+
223
+ **Use \`requirement()\` as the test description** - it returns a string:
224
+
225
+ \`\`\`typescript
226
+ import { describe, it, expect } from 'vitest';
227
+ import { requirement } from '@popoverai/dotrequirements/test';
228
+
229
+ describe(requirement('AUTH-LOGIN-1'), () => {
230
+ describe(requirement('AUTH-LOGIN-1.given'), () => {
231
+ // Arrange: Create Jamie's account
232
+ });
233
+
234
+ describe(requirement('AUTH-LOGIN-1.when'), () => {
235
+ // Act: Submit login with valid credentials
236
+
237
+ it(requirement('AUTH-LOGIN-1.then'), () => {
238
+ // Assert: Jamie is authenticated
239
+ });
240
+ });
241
+ });
242
+ \`\`\`
243
+
244
+ **Test Style Principles:**
245
+
246
+ **1. Use requirement() AS the description**: Don't put requirement() inside test body or in comments.
247
+ - ✅ \`test(requirement('AUTH-LOGIN-1'), () => { /* test code */ })\`
248
+ - ✅ \`it(requirement('LOGIN-1.then'), () => { /* assert */ })\`
249
+ - ❌ \`test("user can log in", () => { requirement('AUTH-LOGIN-1'); /* test code */ })\`
250
+ - ❌ \`// LOGIN-1: User can log in\` (comment instead of using requirement() as description)
251
+
252
+ **2. Comments describe the TEST, not the requirement**: Don't copy requirement text verbatim.
253
+ - ✅ \`describe(requirement('REQ-1.0'), () => { // registered user, valid credentials\`
254
+ - ❌ \`describe(requirement('REQ-1.0'), () => { // 0. When a registered user provides valid credentials, they are authenticated\` (verbatim copy is a red flag)
255
+ - Note: Comments should reflect what the test actually does, not just repeat what the requirement says
256
+
257
+ **3. Structure tests to match requirements**: Nest describe/it blocks for structured requirements.
258
+ - ✅ For structured requirements: \`describe(requirement('LOGIN-1.given'))\` nested with \`describe(requirement('LOGIN-1.when'))\` and \`it(requirement('LOGIN-1.then'))\`
259
+ - ✅ For simple requirements: \`test(requirement('AUTH-LOGIN-1'), () => { /* arrange, act, assert all in one */ })\`
260
+
261
+ **Path formats:**
262
+ - \`requirement('AUTH-LOGIN-1')\` - root requirement
263
+ - \`requirement('AUTH-LOGIN-1.0')\` - by numeric position
264
+ - \`requirement('AUTH-LOGIN-1.given')\` - by label (case-insensitive)
265
+ - \`requirement('AUTH-LOGIN-1.given#1')\` - disambiguate duplicate labels`;
266
+ return template;
267
+ }
268
+ /**
269
+ * Build a complete style-guide document for the calling workspace.
270
+ * Returns a single markdown string suitable for printing to stdout or
271
+ * wrapping in an MCP text response.
272
+ *
273
+ * When `localStyleGuide` is provided and non-empty, that content replaces
274
+ * the bundled default body. The preamble and "Next Steps" footer are
275
+ * always applied.
276
+ */
277
+ export function generateStyleGuide(params) {
278
+ const { filePath = ".requirements/example.requirements.md", localStyleGuide, } = params;
279
+ const body = localStyleGuide?.trim()
280
+ ? localStyleGuide
281
+ : generateStyleGuideBody(params);
282
+ return `# Requirements File Template
283
+
284
+ Here's a comprehensive template for \`${filePath}\` with format and style guidance:
285
+
286
+ \`\`\`markdown
287
+ ${body}
288
+ \`\`\`
289
+
290
+ ## Next Steps
291
+
292
+ 1. **Create file**: Save this template as \`${filePath}\` and edit it for your feature
293
+ 2. **Refine style** (optional): Run \`style-check\` (or call the \`style_check\` MCP tool) for AI feedback
294
+ 3. **Validate syntax**: Run \`validate\` (or call the \`validate\` MCP tool) to verify format
295
+ 4. **Push to cloud**: Run \`dotrequirements push\` (or call the \`push_requirements\` MCP tool) to sync
296
+
297
+ **Note**: Requirements files can be colocated with code (\`src/auth.requirements.md\`) or centralized in \`.requirements/\` directory.`;
298
+ }
299
+ //# sourceMappingURL=style-guide.js.map
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Extract test code context using @babel/parser AST traversal.
3
+ *
4
+ * Finds requirement() call references and extracts the enclosing
5
+ * meaningful code block (function, describe, test, etc.)
6
+ */
7
+ export interface TestCodeReference {
8
+ file: string;
9
+ startLine: number;
10
+ endLine: number;
11
+ code: string;
12
+ requirementId: string;
13
+ }
14
+ /**
15
+ * Find all test code blocks that reference a specific requirement
16
+ */
17
+ export declare function findTestCodeForRequirement(filePath: string, requirementId: string): TestCodeReference[];
18
+ /**
19
+ * Find all files that reference a requirement
20
+ */
21
+ export declare function findFilesWithRequirement(files: string[], requirementId: string): string[];
22
+ //# sourceMappingURL=testCodeExtractor.d.ts.map