@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.
- package/README.md +478 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +82 -0
- package/dist/commands/init.d.ts +6 -0
- package/dist/commands/init.js +355 -0
- package/dist/commands/link.d.ts +15 -0
- package/dist/commands/link.js +156 -0
- package/dist/commands/login.d.ts +12 -0
- package/dist/commands/login.js +117 -0
- package/dist/commands/logout.d.ts +5 -0
- package/dist/commands/logout.js +17 -0
- package/dist/commands/mcp-setup.d.ts +5 -0
- package/dist/commands/mcp-setup.js +367 -0
- package/dist/commands/mcp.d.ts +6 -0
- package/dist/commands/mcp.js +10 -0
- package/dist/commands/pull.d.ts +7 -0
- package/dist/commands/pull.js +171 -0
- package/dist/commands/push.d.ts +11 -0
- package/dist/commands/push.js +310 -0
- package/dist/commands/test.d.ts +6 -0
- package/dist/commands/test.js +78 -0
- package/dist/config.d.ts +5 -0
- package/dist/config.js +16 -0
- package/dist/convex.d.ts +56 -0
- package/dist/convex.js +58 -0
- package/dist/harness/cache.d.ts +135 -0
- package/dist/harness/cache.js +342 -0
- package/dist/harness/convexReporting.d.ts +15 -0
- package/dist/harness/convexReporting.js +136 -0
- package/dist/harness/coverageCache.d.ts +30 -0
- package/dist/harness/coverageCache.js +70 -0
- package/dist/harness/finalize.d.ts +48 -0
- package/dist/harness/finalize.js +299 -0
- package/dist/harness/index.d.ts +70 -0
- package/dist/harness/index.js +103 -0
- package/dist/harness/localReporting.d.ts +6 -0
- package/dist/harness/localReporting.js +49 -0
- package/dist/harness/prepare.d.ts +41 -0
- package/dist/harness/prepare.js +83 -0
- package/dist/harness/requirementsLoader.d.ts +45 -0
- package/dist/harness/requirementsLoader.js +201 -0
- package/dist/harness/tracking.d.ts +49 -0
- package/dist/harness/tracking.js +179 -0
- package/dist/harness/types.d.ts +12 -0
- package/dist/harness/types.js +6 -0
- package/dist/mcp/convexClient.d.ts +43 -0
- package/dist/mcp/convexClient.js +101 -0
- package/dist/mcp/grep.d.ts +24 -0
- package/dist/mcp/grep.js +261 -0
- package/dist/mcp/index.d.ts +3 -0
- package/dist/mcp/index.js +1758 -0
- package/dist/mcp/requirements.d.ts +47 -0
- package/dist/mcp/requirements.js +141 -0
- package/dist/mcp/testCodeExtractor.d.ts +22 -0
- package/dist/mcp/testCodeExtractor.js +152 -0
- package/dist/mcp/types.d.ts +27 -0
- package/dist/mcp/types.js +2 -0
- package/dist/schema/browser.d.ts +12 -0
- package/dist/schema/browser.js +24 -0
- package/dist/schema/builder.d.ts +25 -0
- package/dist/schema/builder.js +125 -0
- package/dist/schema/conversions.d.ts +69 -0
- package/dist/schema/conversions.js +201 -0
- package/dist/schema/index.d.ts +14 -0
- package/dist/schema/index.js +24 -0
- package/dist/schema/parser-core.d.ts +61 -0
- package/dist/schema/parser-core.js +247 -0
- package/dist/schema/parser.d.ts +44 -0
- package/dist/schema/parser.js +295 -0
- package/dist/schema/resolver.d.ts +66 -0
- package/dist/schema/resolver.js +185 -0
- package/dist/schema/schemas.d.ts +312 -0
- package/dist/schema/schemas.js +258 -0
- package/dist/schema/test-schema.d.ts +5 -0
- package/dist/schema/test-schema.js +81 -0
- package/dist/templates/antigravity-gemini.md +3 -0
- package/dist/templates/antigravity-overview-rule.md +3 -0
- package/dist/templates/antigravity-test-rule.md +3 -0
- package/dist/templates/behavioral-core.md +25 -0
- package/dist/templates/claude-code-overview-skill.md +6 -0
- package/dist/templates/claude-code-skill.md +6 -0
- package/dist/templates/claude-code-test-skill.md +6 -0
- package/dist/templates/codex-agents.md +3 -0
- package/dist/templates/codex-overview-agents.md +3 -0
- package/dist/templates/codex-test-agents.md +3 -0
- package/dist/templates/cursor-overview-rule.mdc +5 -0
- package/dist/templates/cursor-rule.mdc +5 -0
- package/dist/templates/cursor-test-rule.mdc +5 -0
- package/dist/templates/example-requirements.d.ts +8 -0
- package/dist/templates/example-requirements.js +88 -0
- package/dist/templates/example-requirements.ts +88 -0
- package/dist/templates/overview-core.md +27 -0
- package/dist/templates/requirements-readme.d.ts +5 -0
- package/dist/templates/requirements-readme.js +31 -0
- package/dist/templates/requirements-readme.ts +30 -0
- package/dist/templates/test-writing-core.md +72 -0
- package/dist/utils/brand.d.ts +5 -0
- package/dist/utils/brand.js +8 -0
- package/dist/utils/browser-launch.d.ts +19 -0
- package/dist/utils/browser-launch.js +36 -0
- package/dist/utils/detect-existing-project.d.ts +5 -0
- package/dist/utils/detect-existing-project.js +34 -0
- package/dist/utils/env.d.ts +19 -0
- package/dist/utils/env.js +56 -0
- package/dist/utils/gitignore.d.ts +7 -0
- package/dist/utils/gitignore.js +29 -0
- package/dist/utils/local-project.d.ts +31 -0
- package/dist/utils/local-project.js +33 -0
- package/dist/utils/oauth-callback-server.d.ts +28 -0
- package/dist/utils/oauth-callback-server.js +156 -0
- package/dist/utils/oauth-flow.d.ts +22 -0
- package/dist/utils/oauth-flow.js +120 -0
- package/dist/utils/project-discovery.d.ts +57 -0
- package/dist/utils/project-discovery.js +146 -0
- package/dist/utils/project-name.d.ts +8 -0
- package/dist/utils/project-name.js +48 -0
- package/dist/utils/project-selector.d.ts +25 -0
- package/dist/utils/project-selector.js +69 -0
- package/dist/utils/prompts.d.ts +33 -0
- package/dist/utils/prompts.js +60 -0
- package/dist/utils/templates.d.ts +29 -0
- package/dist/utils/templates.js +67 -0
- package/dist/utils/token-refresh.d.ts +24 -0
- package/dist/utils/token-refresh.js +69 -0
- package/dist/utils/token-storage.d.ts +31 -0
- package/dist/utils/token-storage.js +57 -0
- package/package.json +82 -0
package/dist/mcp/grep.js
ADDED
|
@@ -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
|