@popoverai/dotrequirements 0.11.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 (127) hide show
  1. package/README.md +478 -0
  2. package/dist/cli.d.ts +3 -0
  3. package/dist/cli.js +82 -0
  4. package/dist/commands/init.d.ts +6 -0
  5. package/dist/commands/init.js +355 -0
  6. package/dist/commands/link.d.ts +15 -0
  7. package/dist/commands/link.js +156 -0
  8. package/dist/commands/login.d.ts +12 -0
  9. package/dist/commands/login.js +117 -0
  10. package/dist/commands/logout.d.ts +5 -0
  11. package/dist/commands/logout.js +17 -0
  12. package/dist/commands/mcp-setup.d.ts +5 -0
  13. package/dist/commands/mcp-setup.js +367 -0
  14. package/dist/commands/mcp.d.ts +6 -0
  15. package/dist/commands/mcp.js +10 -0
  16. package/dist/commands/pull.d.ts +7 -0
  17. package/dist/commands/pull.js +171 -0
  18. package/dist/commands/push.d.ts +11 -0
  19. package/dist/commands/push.js +310 -0
  20. package/dist/commands/test.d.ts +6 -0
  21. package/dist/commands/test.js +78 -0
  22. package/dist/config.d.ts +5 -0
  23. package/dist/config.js +16 -0
  24. package/dist/convex.d.ts +56 -0
  25. package/dist/convex.js +58 -0
  26. package/dist/harness/cache.d.ts +135 -0
  27. package/dist/harness/cache.js +342 -0
  28. package/dist/harness/convexReporting.d.ts +15 -0
  29. package/dist/harness/convexReporting.js +136 -0
  30. package/dist/harness/coverageCache.d.ts +30 -0
  31. package/dist/harness/coverageCache.js +70 -0
  32. package/dist/harness/finalize.d.ts +48 -0
  33. package/dist/harness/finalize.js +299 -0
  34. package/dist/harness/index.d.ts +70 -0
  35. package/dist/harness/index.js +103 -0
  36. package/dist/harness/localReporting.d.ts +6 -0
  37. package/dist/harness/localReporting.js +49 -0
  38. package/dist/harness/prepare.d.ts +41 -0
  39. package/dist/harness/prepare.js +83 -0
  40. package/dist/harness/requirementsLoader.d.ts +45 -0
  41. package/dist/harness/requirementsLoader.js +201 -0
  42. package/dist/harness/tracking.d.ts +49 -0
  43. package/dist/harness/tracking.js +179 -0
  44. package/dist/harness/types.d.ts +12 -0
  45. package/dist/harness/types.js +6 -0
  46. package/dist/mcp/convexClient.d.ts +43 -0
  47. package/dist/mcp/convexClient.js +101 -0
  48. package/dist/mcp/grep.d.ts +24 -0
  49. package/dist/mcp/grep.js +261 -0
  50. package/dist/mcp/index.d.ts +3 -0
  51. package/dist/mcp/index.js +1758 -0
  52. package/dist/mcp/requirements.d.ts +47 -0
  53. package/dist/mcp/requirements.js +141 -0
  54. package/dist/mcp/testCodeExtractor.d.ts +22 -0
  55. package/dist/mcp/testCodeExtractor.js +152 -0
  56. package/dist/mcp/types.d.ts +27 -0
  57. package/dist/mcp/types.js +2 -0
  58. package/dist/schema/browser.d.ts +12 -0
  59. package/dist/schema/browser.js +24 -0
  60. package/dist/schema/builder.d.ts +25 -0
  61. package/dist/schema/builder.js +125 -0
  62. package/dist/schema/conversions.d.ts +69 -0
  63. package/dist/schema/conversions.js +201 -0
  64. package/dist/schema/index.d.ts +14 -0
  65. package/dist/schema/index.js +24 -0
  66. package/dist/schema/parser-core.d.ts +61 -0
  67. package/dist/schema/parser-core.js +247 -0
  68. package/dist/schema/parser.d.ts +44 -0
  69. package/dist/schema/parser.js +295 -0
  70. package/dist/schema/resolver.d.ts +66 -0
  71. package/dist/schema/resolver.js +185 -0
  72. package/dist/schema/schemas.d.ts +312 -0
  73. package/dist/schema/schemas.js +258 -0
  74. package/dist/schema/test-schema.d.ts +5 -0
  75. package/dist/schema/test-schema.js +81 -0
  76. package/dist/templates/antigravity-gemini.md +3 -0
  77. package/dist/templates/antigravity-overview-rule.md +3 -0
  78. package/dist/templates/antigravity-test-rule.md +3 -0
  79. package/dist/templates/behavioral-core.md +25 -0
  80. package/dist/templates/claude-code-overview-skill.md +6 -0
  81. package/dist/templates/claude-code-skill.md +6 -0
  82. package/dist/templates/claude-code-test-skill.md +6 -0
  83. package/dist/templates/codex-agents.md +3 -0
  84. package/dist/templates/codex-overview-agents.md +3 -0
  85. package/dist/templates/codex-test-agents.md +3 -0
  86. package/dist/templates/cursor-overview-rule.mdc +5 -0
  87. package/dist/templates/cursor-rule.mdc +5 -0
  88. package/dist/templates/cursor-test-rule.mdc +5 -0
  89. package/dist/templates/example-requirements.d.ts +8 -0
  90. package/dist/templates/example-requirements.js +88 -0
  91. package/dist/templates/example-requirements.ts +88 -0
  92. package/dist/templates/overview-core.md +27 -0
  93. package/dist/templates/requirements-readme.d.ts +5 -0
  94. package/dist/templates/requirements-readme.js +31 -0
  95. package/dist/templates/requirements-readme.ts +30 -0
  96. package/dist/templates/test-writing-core.md +72 -0
  97. package/dist/utils/brand.d.ts +5 -0
  98. package/dist/utils/brand.js +8 -0
  99. package/dist/utils/browser-launch.d.ts +19 -0
  100. package/dist/utils/browser-launch.js +36 -0
  101. package/dist/utils/detect-existing-project.d.ts +5 -0
  102. package/dist/utils/detect-existing-project.js +34 -0
  103. package/dist/utils/env.d.ts +19 -0
  104. package/dist/utils/env.js +56 -0
  105. package/dist/utils/gitignore.d.ts +7 -0
  106. package/dist/utils/gitignore.js +29 -0
  107. package/dist/utils/local-project.d.ts +31 -0
  108. package/dist/utils/local-project.js +33 -0
  109. package/dist/utils/oauth-callback-server.d.ts +28 -0
  110. package/dist/utils/oauth-callback-server.js +156 -0
  111. package/dist/utils/oauth-flow.d.ts +22 -0
  112. package/dist/utils/oauth-flow.js +120 -0
  113. package/dist/utils/project-discovery.d.ts +57 -0
  114. package/dist/utils/project-discovery.js +146 -0
  115. package/dist/utils/project-name.d.ts +8 -0
  116. package/dist/utils/project-name.js +48 -0
  117. package/dist/utils/project-selector.d.ts +25 -0
  118. package/dist/utils/project-selector.js +69 -0
  119. package/dist/utils/prompts.d.ts +33 -0
  120. package/dist/utils/prompts.js +60 -0
  121. package/dist/utils/templates.d.ts +29 -0
  122. package/dist/utils/templates.js +67 -0
  123. package/dist/utils/token-refresh.d.ts +24 -0
  124. package/dist/utils/token-refresh.js +69 -0
  125. package/dist/utils/token-storage.d.ts +31 -0
  126. package/dist/utils/token-storage.js +57 -0
  127. package/package.json +82 -0
@@ -0,0 +1,261 @@
1
+ import { execFile } from 'child_process';
2
+ import { promisify } from 'util';
3
+ const execFileAsync = promisify(execFile);
4
+ /**
5
+ * Check if ripgrep is available
6
+ */
7
+ async function hasRipgrep() {
8
+ try {
9
+ await execFileAsync('rg', ['--version']);
10
+ return true;
11
+ }
12
+ catch {
13
+ return false;
14
+ }
15
+ }
16
+ /**
17
+ * Find all test files that reference a specific requirement ID
18
+ *
19
+ * Uses execFile with array arguments to avoid command injection vulnerabilities.
20
+ */
21
+ export async function findTestsForRequirement(workspaceRoot, requirementId) {
22
+ const useRg = await hasRipgrep();
23
+ // Escape special regex characters in the ID
24
+ const escapedId = requirementId.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
25
+ // Pattern to match requirement('ID'), requirement("ID"), or requirement(`ID`)
26
+ const pattern = `requirement\\s*\\(\\s*['"\`]${escapedId}`;
27
+ try {
28
+ let output;
29
+ if (useRg) {
30
+ // Use ripgrep for better performance
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
33
+ output = stdout;
34
+ }
35
+ else {
36
+ // 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
38
+ output = stdout;
39
+ }
40
+ return parseGrepOutput(output, requirementId, useRg);
41
+ }
42
+ catch (error) {
43
+ // Grep returns exit code 1 when no matches found
44
+ return [];
45
+ }
46
+ }
47
+ /**
48
+ * Find all requirement references in a specific file using AST parsing
49
+ */
50
+ 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');
56
+ const results = [];
57
+ // Resolve relative paths from current working directory
58
+ let resolvedPath = pathModule.isAbsolute(filePath)
59
+ ? filePath
60
+ : pathModule.resolve(process.cwd(), filePath);
61
+ // If file doesn't exist and path is relative, try resolving from CLI package root
62
+ if (!pathModule.isAbsolute(filePath) && !fs.existsSync(resolvedPath)) {
63
+ const cliPackageRoot = pathModule.resolve(__dirname, '../..');
64
+ const alternativePath = pathModule.resolve(cliPackageRoot, filePath);
65
+ if (fs.existsSync(alternativePath)) {
66
+ resolvedPath = alternativePath;
67
+ }
68
+ }
69
+ try {
70
+ const code = fs.readFileSync(resolvedPath, 'utf-8');
71
+ const ast = parse(code, {
72
+ sourceType: 'module',
73
+ plugins: ['typescript', 'jsx'],
74
+ errorRecovery: true,
75
+ });
76
+ // Handle both ES module and CommonJS exports for traverse
77
+ let traverseFunc = traverse;
78
+ if (typeof traverseFunc !== 'function') {
79
+ traverseFunc = traverseFunc.default;
80
+ }
81
+ if (typeof traverseFunc !== 'function') {
82
+ traverseFunc = traverseFunc.default;
83
+ }
84
+ traverseFunc(ast, {
85
+ CallExpression(path) {
86
+ const callee = path.node.callee;
87
+ // Check if this is a requirement() call
88
+ if (t.isIdentifier(callee) && callee.name === 'requirement') {
89
+ const args = path.node.arguments;
90
+ const loc = path.node.loc;
91
+ // Extract all string literal arguments (supports multi-requirement calls)
92
+ for (const arg of args) {
93
+ if (t.isStringLiteral(arg)) {
94
+ const requirementId = arg.value;
95
+ if (loc) {
96
+ results.push({
97
+ file: resolvedPath,
98
+ line: loc.start.line,
99
+ column: loc.start.column + 1, // 1-indexed
100
+ requirementId,
101
+ context: code.split('\n')[loc.start.line - 1] || '',
102
+ });
103
+ }
104
+ }
105
+ }
106
+ }
107
+ },
108
+ });
109
+ return results;
110
+ }
111
+ catch (error) {
112
+ return [];
113
+ }
114
+ }
115
+ /**
116
+ * Find all requirement references in the entire workspace
117
+ *
118
+ * Uses execFile with array arguments to avoid command injection vulnerabilities.
119
+ */
120
+ export async function findAllTestReferences(workspaceRoot) {
121
+ const useRg = await hasRipgrep();
122
+ // Pattern to match any requirement() call
123
+ const pattern = `requirement\\s*\\(\\s*['"\`][^'"\`]+`;
124
+ try {
125
+ let output;
126
+ if (useRg) {
127
+ // 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
129
+ output = stdout;
130
+ }
131
+ 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
133
+ output = stdout;
134
+ }
135
+ return parseGrepOutput(output, '', useRg);
136
+ }
137
+ catch {
138
+ return [];
139
+ }
140
+ }
141
+ /**
142
+ * Parse grep/ripgrep output into TestReference objects
143
+ */
144
+ function parseGrepOutput(output, expectedId, isRipgrep, singleFilePath) {
145
+ const results = [];
146
+ const lines = output.trim().split('\n').filter(Boolean);
147
+ for (const line of lines) {
148
+ let match;
149
+ let file;
150
+ let lineNum;
151
+ let column;
152
+ let context;
153
+ if (isRipgrep) {
154
+ // ripgrep format: file:line:column:content
155
+ match = line.match(/^(.+?):(\d+):(\d+):(.*)$/);
156
+ if (!match)
157
+ continue;
158
+ file = match[1];
159
+ lineNum = parseInt(match[2], 10);
160
+ column = parseInt(match[3], 10);
161
+ context = match[4];
162
+ }
163
+ else {
164
+ // grep format: file:line:content (no column)
165
+ if (singleFilePath) {
166
+ match = line.match(/^(\d+):(.*)$/);
167
+ if (!match)
168
+ continue;
169
+ file = singleFilePath;
170
+ lineNum = parseInt(match[1], 10);
171
+ context = match[2];
172
+ column = 1;
173
+ }
174
+ else {
175
+ match = line.match(/^(.+?):(\d+):(.*)$/);
176
+ if (!match)
177
+ continue;
178
+ file = match[1];
179
+ lineNum = parseInt(match[2], 10);
180
+ context = match[3];
181
+ column = 1;
182
+ }
183
+ }
184
+ // Extract the requirement ID from the context
185
+ const idMatch = context.match(/requirement\s*\(\s*['"`]([^'"`]+)/);
186
+ const requirementId = idMatch ? idMatch[1] : expectedId;
187
+ results.push({
188
+ file,
189
+ line: lineNum,
190
+ column,
191
+ requirementId,
192
+ context: context.trim(),
193
+ });
194
+ }
195
+ return results;
196
+ }
197
+ /**
198
+ * Get all unique requirement IDs referenced in tests
199
+ * Normalizes child IDs (e.g., AUTH-VALID-LOGIN.0) to root IDs (e.g., AUTH-VALID-LOGIN)
200
+ * Uses AST parsing instead of grep for reliability across environments
201
+ */
202
+ 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');
208
+ // Find all test files using glob (same pattern as get_requirement tool)
209
+ const testFiles = await glob('**/*.{test,spec}.{js,jsx,ts,tsx}', {
210
+ cwd: workspaceRoot,
211
+ absolute: true,
212
+ ignore: ['**/node_modules/**', '**/dist/**', '**/build/**'],
213
+ });
214
+ const allRequirementIds = [];
215
+ // For each test file, parse AST and extract requirement IDs
216
+ for (const file of testFiles) {
217
+ try {
218
+ const code = fs.readFileSync(file, 'utf-8');
219
+ const ast = parse(code, {
220
+ sourceType: 'module',
221
+ plugins: ['typescript', 'jsx'],
222
+ errorRecovery: true,
223
+ });
224
+ // Handle both ES module and CommonJS exports for traverse
225
+ // @babel/traverse can be imported as: traverse.default.default in compiled code
226
+ let traverseFunc = traverse;
227
+ if (typeof traverseFunc !== 'function') {
228
+ traverseFunc = traverseFunc.default;
229
+ }
230
+ if (typeof traverseFunc !== 'function') {
231
+ traverseFunc = traverseFunc.default;
232
+ }
233
+ traverseFunc(ast, {
234
+ CallExpression(path) {
235
+ const callee = path.node.callee;
236
+ // Check if this is a requirement() call
237
+ if (t.isIdentifier(callee) && callee.name === 'requirement') {
238
+ const args = path.node.arguments;
239
+ // Extract all string literal arguments (supports multi-requirement calls)
240
+ for (const arg of args) {
241
+ if (t.isStringLiteral(arg)) {
242
+ allRequirementIds.push(arg.value);
243
+ }
244
+ }
245
+ }
246
+ },
247
+ });
248
+ }
249
+ catch (error) {
250
+ // Skip unparseable files
251
+ continue;
252
+ }
253
+ }
254
+ // Extract root IDs by removing the path suffix (everything after the first dot)
255
+ const rootIds = allRequirementIds.map(id => {
256
+ const dotIndex = id.indexOf('.');
257
+ return dotIndex > 0 ? id.substring(0, dotIndex) : id;
258
+ });
259
+ return new Set(rootIds);
260
+ }
261
+ //# sourceMappingURL=grep.js.map
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map