@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
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { type RequirementNode, type Metadata } from '../schema/index.js';
|
|
2
|
+
import type { FlattenedRequirement } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Parsed requirements file
|
|
5
|
+
*/
|
|
6
|
+
export interface ParsedRequirementsFile {
|
|
7
|
+
metadata: Metadata;
|
|
8
|
+
requirements: RequirementNode[];
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Find all *.requirements.md files in the workspace.
|
|
12
|
+
* Supports both .requirements/ directories and colocated files.
|
|
13
|
+
* Excludes example files, test fixtures, and build artifacts.
|
|
14
|
+
*/
|
|
15
|
+
export declare function findRequirementsFiles(workspaceRoot: string): Promise<string[]>;
|
|
16
|
+
/**
|
|
17
|
+
* Load all requirements from the workspace
|
|
18
|
+
*/
|
|
19
|
+
export declare function loadAllRequirements(workspaceRoot: string): Promise<{
|
|
20
|
+
files: ParsedRequirementsFile[];
|
|
21
|
+
flattened: FlattenedRequirement[];
|
|
22
|
+
}>;
|
|
23
|
+
/**
|
|
24
|
+
* Flatten a requirements file into searchable requirements
|
|
25
|
+
*/
|
|
26
|
+
export declare function flattenRequirementsFile(file: ParsedRequirementsFile, sourceFile: string): FlattenedRequirement[];
|
|
27
|
+
/**
|
|
28
|
+
* Search requirements by text query
|
|
29
|
+
*/
|
|
30
|
+
export declare function searchRequirements(requirements: FlattenedRequirement[], query: string): FlattenedRequirement[];
|
|
31
|
+
/**
|
|
32
|
+
* Get a specific requirement by ID (supports partial matching)
|
|
33
|
+
*/
|
|
34
|
+
export declare function getRequirementById(requirements: FlattenedRequirement[], id: string): FlattenedRequirement | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* Get a requirement and all its children
|
|
37
|
+
*/
|
|
38
|
+
export declare function getRequirementTree(requirements: FlattenedRequirement[], rootId: string): FlattenedRequirement[];
|
|
39
|
+
/**
|
|
40
|
+
* Format a requirement for display
|
|
41
|
+
*/
|
|
42
|
+
export declare function formatRequirement(req: FlattenedRequirement): string;
|
|
43
|
+
/**
|
|
44
|
+
* Format a requirement tree for display
|
|
45
|
+
*/
|
|
46
|
+
export declare function formatRequirementTree(requirements: FlattenedRequirement[]): string;
|
|
47
|
+
//# sourceMappingURL=requirements.d.ts.map
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import * as path from 'path';
|
|
2
|
+
import { glob } from 'glob';
|
|
3
|
+
import { parseRequirementsFromFile, getAllRequirements, } from '../schema/index.js';
|
|
4
|
+
/**
|
|
5
|
+
* Find all *.requirements.md files in the workspace.
|
|
6
|
+
* Supports both .requirements/ directories and colocated files.
|
|
7
|
+
* Excludes example files, test fixtures, and build artifacts.
|
|
8
|
+
*/
|
|
9
|
+
export async function findRequirementsFiles(workspaceRoot) {
|
|
10
|
+
const pattern = '**/*.requirements.md';
|
|
11
|
+
const matches = await glob(pattern, {
|
|
12
|
+
cwd: workspaceRoot,
|
|
13
|
+
ignore: [
|
|
14
|
+
'**/node_modules/**',
|
|
15
|
+
'**/dist/**',
|
|
16
|
+
'**/.git/**',
|
|
17
|
+
'**/build/**',
|
|
18
|
+
// Exclude example and test fixture directories
|
|
19
|
+
'**/example/**',
|
|
20
|
+
'**/examples/**',
|
|
21
|
+
'**/__tests__/**',
|
|
22
|
+
'**/fixtures/**',
|
|
23
|
+
'**/.fixtures/**',
|
|
24
|
+
],
|
|
25
|
+
dot: true, // Include dotfiles/dotdirs like .requirements/
|
|
26
|
+
});
|
|
27
|
+
return matches.map(m => path.join(workspaceRoot, m));
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Load all requirements from the workspace
|
|
31
|
+
*/
|
|
32
|
+
export async function loadAllRequirements(workspaceRoot) {
|
|
33
|
+
const filePaths = await findRequirementsFiles(workspaceRoot);
|
|
34
|
+
const allFiles = [];
|
|
35
|
+
const allFlattened = [];
|
|
36
|
+
for (const filePath of filePaths) {
|
|
37
|
+
try {
|
|
38
|
+
const parsed = parseRequirementsFromFile(filePath);
|
|
39
|
+
allFiles.push(parsed);
|
|
40
|
+
const flattened = flattenRequirementsFile(parsed, filePath);
|
|
41
|
+
allFlattened.push(...flattened);
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
// Skip invalid files
|
|
45
|
+
console.error(`Failed to parse ${filePath}:`, error);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return { files: allFiles, flattened: allFlattened };
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Flatten a requirements file into searchable requirements
|
|
52
|
+
*/
|
|
53
|
+
export function flattenRequirementsFile(file, sourceFile) {
|
|
54
|
+
const results = [];
|
|
55
|
+
// Use shared utility to get all requirements (flattened)
|
|
56
|
+
const allReqs = getAllRequirements(file.requirements);
|
|
57
|
+
for (const req of allReqs) {
|
|
58
|
+
// Extract position from ID (e.g., "REQ123.0.1" -> "0.1")
|
|
59
|
+
const idParts = req.id.split('.');
|
|
60
|
+
const rootId = idParts[0];
|
|
61
|
+
const position = idParts.slice(1).join('.');
|
|
62
|
+
results.push({
|
|
63
|
+
id: req.id,
|
|
64
|
+
rootId: rootId,
|
|
65
|
+
label: req.label,
|
|
66
|
+
content: req.content,
|
|
67
|
+
path: idParts.slice(1), // ["0", "1"] for "REQ123.0.1"
|
|
68
|
+
sourceFile: sourceFile,
|
|
69
|
+
documentTitle: file.metadata.document?.title || rootId,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
return results;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Search requirements by text query
|
|
76
|
+
*/
|
|
77
|
+
export function searchRequirements(requirements, query) {
|
|
78
|
+
const lowerQuery = query.toLowerCase();
|
|
79
|
+
return requirements.filter(req => {
|
|
80
|
+
// Search in ID
|
|
81
|
+
if (req.id.toLowerCase().includes(lowerQuery))
|
|
82
|
+
return true;
|
|
83
|
+
// Search in content
|
|
84
|
+
if (req.content.toLowerCase().includes(lowerQuery))
|
|
85
|
+
return true;
|
|
86
|
+
// Search in label
|
|
87
|
+
if (req.label.toLowerCase().includes(lowerQuery))
|
|
88
|
+
return true;
|
|
89
|
+
return false;
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Get a specific requirement by ID (supports partial matching)
|
|
94
|
+
*/
|
|
95
|
+
export function getRequirementById(requirements, id) {
|
|
96
|
+
// Exact match first
|
|
97
|
+
const exact = requirements.find(r => r.id === id);
|
|
98
|
+
if (exact)
|
|
99
|
+
return exact;
|
|
100
|
+
// Try partial match (just the root ID)
|
|
101
|
+
return requirements.find(r => r.rootId === id || r.id.startsWith(id + '.'));
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Get a requirement and all its children
|
|
105
|
+
*/
|
|
106
|
+
export function getRequirementTree(requirements, rootId) {
|
|
107
|
+
return requirements.filter(r => r.rootId === rootId || r.id === rootId);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Format a requirement for display
|
|
111
|
+
*/
|
|
112
|
+
export function formatRequirement(req) {
|
|
113
|
+
const indent = ' '.repeat(req.path.length);
|
|
114
|
+
const label = req.label ? ` (${req.label})` : '';
|
|
115
|
+
return `${indent}${req.id}${label}: ${req.content}`;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Format a requirement tree for display
|
|
119
|
+
*/
|
|
120
|
+
export function formatRequirementTree(requirements) {
|
|
121
|
+
// Sort by path to maintain hierarchy - children appear immediately after parent
|
|
122
|
+
const sorted = [...requirements].sort((a, b) => {
|
|
123
|
+
// Compare paths element by element for proper tree order
|
|
124
|
+
const maxLen = Math.max(a.path.length, b.path.length);
|
|
125
|
+
for (let i = 0; i < maxLen; i++) {
|
|
126
|
+
const aVal = i < a.path.length ? parseInt(a.path[i]) : -1;
|
|
127
|
+
const bVal = i < b.path.length ? parseInt(b.path[i]) : -1;
|
|
128
|
+
// If one path is a prefix of the other, the shorter one comes first
|
|
129
|
+
if (aVal === -1)
|
|
130
|
+
return -1;
|
|
131
|
+
if (bVal === -1)
|
|
132
|
+
return 1;
|
|
133
|
+
if (aVal !== bVal) {
|
|
134
|
+
return aVal - bVal;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return 0;
|
|
138
|
+
});
|
|
139
|
+
return sorted.map(formatRequirement).join('\n');
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=requirements.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
|
|
@@ -0,0 +1,152 @@
|
|
|
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
|
+
import { parse } from '@babel/parser';
|
|
8
|
+
import traverse from '@babel/traverse';
|
|
9
|
+
import * as t from '@babel/types';
|
|
10
|
+
import * as fs from 'fs';
|
|
11
|
+
/**
|
|
12
|
+
* Find all test code blocks that reference a specific requirement
|
|
13
|
+
*/
|
|
14
|
+
export function findTestCodeForRequirement(filePath, requirementId) {
|
|
15
|
+
try {
|
|
16
|
+
const code = fs.readFileSync(filePath, 'utf-8');
|
|
17
|
+
const results = [];
|
|
18
|
+
// Parse with TypeScript and JSX support
|
|
19
|
+
const ast = parse(code, {
|
|
20
|
+
sourceType: 'module',
|
|
21
|
+
plugins: ['typescript', 'jsx'],
|
|
22
|
+
errorRecovery: true,
|
|
23
|
+
});
|
|
24
|
+
// Handle both ES module and CommonJS exports
|
|
25
|
+
const traverseFunc = typeof traverse === 'function' ? traverse : traverse.default;
|
|
26
|
+
traverseFunc(ast, {
|
|
27
|
+
CallExpression(path) {
|
|
28
|
+
// Check if this is a requirement('ID', ...) call
|
|
29
|
+
const callee = path.node.callee;
|
|
30
|
+
if (t.isIdentifier(callee) && callee.name === 'requirement') {
|
|
31
|
+
const args = path.node.arguments;
|
|
32
|
+
// Extract all string literal arguments (supports multi-requirement calls)
|
|
33
|
+
const refIds = [];
|
|
34
|
+
for (const arg of args) {
|
|
35
|
+
if (t.isStringLiteral(arg)) {
|
|
36
|
+
refIds.push(arg.value);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
// Check if any argument matches the requirement we're looking for
|
|
40
|
+
// Match exact ID or if it's a child (e.g., searching for REQ-123 matches REQ-123.0)
|
|
41
|
+
for (const refId of refIds) {
|
|
42
|
+
const isMatch = refId === requirementId ||
|
|
43
|
+
refId.startsWith(`${requirementId}.`);
|
|
44
|
+
if (isMatch) {
|
|
45
|
+
// Find the meaningful enclosing block
|
|
46
|
+
const contextNode = findMeaningfulParent(path);
|
|
47
|
+
if (contextNode && contextNode.node.loc) {
|
|
48
|
+
const { start, end } = contextNode.node.loc;
|
|
49
|
+
const nodeStart = contextNode.node.start ?? 0;
|
|
50
|
+
const nodeEnd = contextNode.node.end ?? code.length;
|
|
51
|
+
results.push({
|
|
52
|
+
file: filePath,
|
|
53
|
+
startLine: start.line,
|
|
54
|
+
endLine: end.line,
|
|
55
|
+
code: code.slice(nodeStart, nodeEnd),
|
|
56
|
+
requirementId: refId,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
// Only add one result per call expression, even if multiple refs match
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
return results;
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
console.error(`Error parsing ${filePath}:`, error);
|
|
70
|
+
return [];
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Walk up the AST to find the nearest "meaningful" parent node.
|
|
75
|
+
* This could be a function, describe/test/it call, method, etc.
|
|
76
|
+
*/
|
|
77
|
+
function findMeaningfulParent(path) {
|
|
78
|
+
let current = path;
|
|
79
|
+
while (current) {
|
|
80
|
+
const node = current.node;
|
|
81
|
+
// Check for various meaningful boundaries
|
|
82
|
+
if (t.isFunctionDeclaration(node) ||
|
|
83
|
+
t.isFunctionExpression(node) ||
|
|
84
|
+
t.isArrowFunctionExpression(node) ||
|
|
85
|
+
t.isClassMethod(node) ||
|
|
86
|
+
t.isObjectMethod(node)) {
|
|
87
|
+
return current;
|
|
88
|
+
}
|
|
89
|
+
// Check for call expressions (describe, test, it, etc.)
|
|
90
|
+
if (t.isCallExpression(node)) {
|
|
91
|
+
const callee = node.callee;
|
|
92
|
+
// Common test framework function names
|
|
93
|
+
const testFunctionNames = [
|
|
94
|
+
'describe',
|
|
95
|
+
'it',
|
|
96
|
+
'test',
|
|
97
|
+
'suite',
|
|
98
|
+
'context',
|
|
99
|
+
'beforeEach',
|
|
100
|
+
'afterEach',
|
|
101
|
+
'beforeAll',
|
|
102
|
+
'afterAll',
|
|
103
|
+
];
|
|
104
|
+
if (t.isIdentifier(callee) && testFunctionNames.includes(callee.name)) {
|
|
105
|
+
return current;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// Check for variable declarations that might contain test functions
|
|
109
|
+
if (t.isVariableDeclarator(node)) {
|
|
110
|
+
return current;
|
|
111
|
+
}
|
|
112
|
+
// Check for export declarations (for Storybook stories, etc.)
|
|
113
|
+
if (t.isExportNamedDeclaration(node) ||
|
|
114
|
+
t.isExportDefaultDeclaration(node)) {
|
|
115
|
+
return current;
|
|
116
|
+
}
|
|
117
|
+
current = current.parentPath;
|
|
118
|
+
}
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Find all files that reference a requirement
|
|
123
|
+
*/
|
|
124
|
+
export function findFilesWithRequirement(files, requirementId) {
|
|
125
|
+
const matchingFiles = [];
|
|
126
|
+
for (const file of files) {
|
|
127
|
+
try {
|
|
128
|
+
const code = fs.readFileSync(file, 'utf-8');
|
|
129
|
+
// Check for exact match or child references (e.g., AUTH-VALID-LOGIN or AUTH-VALID-LOGIN.0)
|
|
130
|
+
// Simple string search first for performance
|
|
131
|
+
// Patterns match both single and multi-arg calls:
|
|
132
|
+
// requirement('REQ-123')
|
|
133
|
+
// requirement('REQ-123', 'REQ-456')
|
|
134
|
+
// requirement('REQ-456', 'REQ-123') <-- ID anywhere in args
|
|
135
|
+
const hasMatch = code.includes(`'${requirementId}'`) ||
|
|
136
|
+
code.includes(`"${requirementId}"`) ||
|
|
137
|
+
code.includes(`\`${requirementId}\``) ||
|
|
138
|
+
code.includes(`'${requirementId}.`) ||
|
|
139
|
+
code.includes(`"${requirementId}.`) ||
|
|
140
|
+
code.includes(`\`${requirementId}.`);
|
|
141
|
+
if (hasMatch) {
|
|
142
|
+
matchingFiles.push(file);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
// Skip files that can't be read
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return matchingFiles;
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=testCodeExtractor.js.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Re-export shared types from CLI schema module
|
|
3
|
+
*/
|
|
4
|
+
export type { Metadata, RequirementNode, RequirementsFile, } from '../schema/index.js';
|
|
5
|
+
/**
|
|
6
|
+
* Flattened requirement for search results
|
|
7
|
+
*/
|
|
8
|
+
export interface FlattenedRequirement {
|
|
9
|
+
id: string;
|
|
10
|
+
rootId: string;
|
|
11
|
+
label: string;
|
|
12
|
+
content: string;
|
|
13
|
+
path: string[];
|
|
14
|
+
sourceFile: string;
|
|
15
|
+
documentTitle: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Test reference found in codebase
|
|
19
|
+
*/
|
|
20
|
+
export interface TestReference {
|
|
21
|
+
file: string;
|
|
22
|
+
line: number;
|
|
23
|
+
column: number;
|
|
24
|
+
requirementId: string;
|
|
25
|
+
context: string;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-safe exports from the schema module.
|
|
3
|
+
* This file excludes Node.js-specific functionality (file I/O, fs module).
|
|
4
|
+
* Use this entry point when importing from browser/web environments.
|
|
5
|
+
*/
|
|
6
|
+
export { MetadataSchema, RequirementNodeSchema, RequirementsFileSchema, ParsedCriterionSchema, RequirementPrefixSchema, RequirementKeySchema, REQUIREMENT_PREFIX_PATTERN, REQUIREMENT_KEY_PATTERN, ValidationError, validateMetadata, validateRequirementNode, validateRequirementsFile, validatePrefix, validateKey, normalizePrefix, parseRequirementKey, buildRequirementKey, } from './schemas.js';
|
|
7
|
+
export type { Metadata, RequirementNode, RequirementsFile, ParsedCriterion, RequirementPrefix, RequirementKey, } from './schemas.js';
|
|
8
|
+
export { DEFAULT_DELIMITER, buildRequirementsMarkdown, buildRequirementMarkdown, } from './builder.js';
|
|
9
|
+
export type { ConvexRequirement, } from './conversions.js';
|
|
10
|
+
export { convexToRequirements, requirementsToConvex, buildMetadata, extractRequirementKeys, groupByRoot, constructKey, parseKey, } from './conversions.js';
|
|
11
|
+
export { DELIMITER_PATTERN, parseCriterionLine, parseRootLine, parseRequirementBlock, extractRequirementBlocks, parseRequirementBlocksFromMarkdown, flattenRequirementTree, findRequirementById, getAllRequirements, } from './parser-core.js';
|
|
12
|
+
//# sourceMappingURL=browser.d.ts.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-safe exports from the schema module.
|
|
3
|
+
* This file excludes Node.js-specific functionality (file I/O, fs module).
|
|
4
|
+
* Use this entry point when importing from browser/web environments.
|
|
5
|
+
*/
|
|
6
|
+
// Schemas and types (uses zod - browser-safe)
|
|
7
|
+
export {
|
|
8
|
+
// Zod schemas
|
|
9
|
+
MetadataSchema, RequirementNodeSchema, RequirementsFileSchema, ParsedCriterionSchema, RequirementPrefixSchema, RequirementKeySchema,
|
|
10
|
+
// Patterns (for external validation)
|
|
11
|
+
REQUIREMENT_PREFIX_PATTERN, REQUIREMENT_KEY_PATTERN,
|
|
12
|
+
// Validation
|
|
13
|
+
ValidationError, validateMetadata, validateRequirementNode, validateRequirementsFile, validatePrefix, validateKey,
|
|
14
|
+
// Prefix/key utilities
|
|
15
|
+
normalizePrefix, parseRequirementKey, buildRequirementKey, } from './schemas.js';
|
|
16
|
+
// Building (uses yaml package - browser-safe)
|
|
17
|
+
export { DEFAULT_DELIMITER, buildRequirementsMarkdown, buildRequirementMarkdown, } from './builder.js';
|
|
18
|
+
export { convexToRequirements, requirementsToConvex, buildMetadata, extractRequirementKeys, groupByRoot, constructKey, parseKey, } from './conversions.js';
|
|
19
|
+
// Parser core (pure TypeScript - browser-safe, no fs dependency)
|
|
20
|
+
// These functions parse markdown strings directly without file I/O
|
|
21
|
+
export { DELIMITER_PATTERN, parseCriterionLine, parseRootLine, parseRequirementBlock, extractRequirementBlocks, parseRequirementBlocksFromMarkdown, flattenRequirementTree, findRequirementById, getAllRequirements, } from './parser-core.js';
|
|
22
|
+
// NOTE: parser.ts and resolver.ts are excluded because they use Node.js 'fs' module.
|
|
23
|
+
// Use parser-core.ts functions above for browser/Convex environments.
|
|
24
|
+
//# sourceMappingURL=browser.js.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build Markdown requirements files from structured data.
|
|
3
|
+
*/
|
|
4
|
+
import { RequirementNode, Metadata } from './schemas.js';
|
|
5
|
+
/**
|
|
6
|
+
* Default delimiter for requirements.
|
|
7
|
+
* Can be overridden for organization-specific preferences.
|
|
8
|
+
*/
|
|
9
|
+
export declare const DEFAULT_DELIMITER = "\u2192";
|
|
10
|
+
/**
|
|
11
|
+
* Build complete Markdown requirements document.
|
|
12
|
+
*/
|
|
13
|
+
export declare function buildRequirementsMarkdown(metadata: Metadata, requirements: RequirementNode[], documentTitle?: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* Build Markdown for a single requirement (for testing/debugging).
|
|
16
|
+
*/
|
|
17
|
+
export declare function buildRequirementMarkdown(node: RequirementNode): string;
|
|
18
|
+
/**
|
|
19
|
+
* Build a complete requirements file from metadata and raw markdown content.
|
|
20
|
+
* Used by CLI pull to combine frontmatter with cloud-sourced markdown.
|
|
21
|
+
*
|
|
22
|
+
* SYNC-ARCH-1: markdownContent is the source of truth from the cloud.
|
|
23
|
+
*/
|
|
24
|
+
export declare function buildRequirementsFile(metadata: Metadata, markdownContent: string): string;
|
|
25
|
+
//# sourceMappingURL=builder.d.ts.map
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build Markdown requirements files from structured data.
|
|
3
|
+
*/
|
|
4
|
+
import YAML from 'yaml';
|
|
5
|
+
/**
|
|
6
|
+
* Default delimiter for requirements.
|
|
7
|
+
* Can be overridden for organization-specific preferences.
|
|
8
|
+
*/
|
|
9
|
+
export const DEFAULT_DELIMITER = '→';
|
|
10
|
+
/**
|
|
11
|
+
* Build YAML frontmatter from metadata.
|
|
12
|
+
*/
|
|
13
|
+
function buildFrontmatter(metadata) {
|
|
14
|
+
const yamlStr = YAML.stringify(metadata, {
|
|
15
|
+
lineWidth: 0, // Don't wrap lines
|
|
16
|
+
minContentWidth: 0,
|
|
17
|
+
});
|
|
18
|
+
return `---\n${yamlStr.trim()}\n---`;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Build a dotrequirements block from a requirement node.
|
|
22
|
+
* Formats children with explicit position paths and proper indentation.
|
|
23
|
+
*/
|
|
24
|
+
function buildRequirementBlock(node, delimiter = DEFAULT_DELIMITER) {
|
|
25
|
+
// First line: root requirement with delimiter
|
|
26
|
+
// Include label if it's not empty (empty string means unlabeled)
|
|
27
|
+
const rootLabel = node.label
|
|
28
|
+
? `${node.label.charAt(0).toUpperCase() + node.label.slice(1)} `
|
|
29
|
+
: '';
|
|
30
|
+
let block = `${rootLabel}${delimiter} ${node.content}\n`;
|
|
31
|
+
// Recursively build criteria lines
|
|
32
|
+
function addCriteria(children, parentPosition = '') {
|
|
33
|
+
children.forEach((child, index) => {
|
|
34
|
+
const position = parentPosition === '' ? `${index}` : `${parentPosition}.${index}`;
|
|
35
|
+
const depth = position.split('.').length - 1;
|
|
36
|
+
const indent = ' '.repeat(depth + 1); // 2 spaces per level
|
|
37
|
+
// Include label if present, otherwise just delimiter
|
|
38
|
+
const labelPart = child.label
|
|
39
|
+
? `${child.label.charAt(0).toUpperCase() + child.label.slice(1)} `
|
|
40
|
+
: '';
|
|
41
|
+
block += `${indent}${position}. ${labelPart}${delimiter} ${child.content}\n`;
|
|
42
|
+
// Recursively add grandchildren
|
|
43
|
+
if (child.children && child.children.length > 0) {
|
|
44
|
+
addCriteria(child.children, position);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
addCriteria(node.children);
|
|
49
|
+
return block.trimEnd();
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Build the children portion of a requirement block (without the root line).
|
|
53
|
+
*/
|
|
54
|
+
function buildChildrenBlock(children) {
|
|
55
|
+
let block = '';
|
|
56
|
+
function addChildren(nodes, parentPosition = '') {
|
|
57
|
+
nodes.forEach((child, index) => {
|
|
58
|
+
const position = parentPosition === '' ? `${index}` : `${parentPosition}.${index}`;
|
|
59
|
+
const depth = position.split('.').length - 1;
|
|
60
|
+
const indent = ' '.repeat(depth + 1); // 2 spaces per level
|
|
61
|
+
// Include label if present, otherwise just delimiter
|
|
62
|
+
const labelPart = child.label
|
|
63
|
+
? `${child.label.charAt(0).toUpperCase() + child.label.slice(1)} `
|
|
64
|
+
: '';
|
|
65
|
+
block += `${indent}${position}. ${labelPart}→ ${child.content}\n`;
|
|
66
|
+
// Recursively add grandchildren
|
|
67
|
+
if (child.children && child.children.length > 0) {
|
|
68
|
+
addChildren(child.children, position);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
addChildren(children);
|
|
73
|
+
return block.trimEnd();
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Build a complete requirement section (heading + block).
|
|
77
|
+
*/
|
|
78
|
+
function buildRequirementSection(node, title) {
|
|
79
|
+
const displayTitle = title || node.content;
|
|
80
|
+
// Heading now just has the title, no key
|
|
81
|
+
let section = `## ${displayTitle}\n\n`;
|
|
82
|
+
section += '```dotrequirements\n';
|
|
83
|
+
// First line of block has the key
|
|
84
|
+
section += `${node.id}: ${node.content}\n`;
|
|
85
|
+
// Then add children
|
|
86
|
+
if (node.children && node.children.length > 0) {
|
|
87
|
+
section += buildChildrenBlock(node.children);
|
|
88
|
+
}
|
|
89
|
+
section += '\n```\n';
|
|
90
|
+
return section;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Build complete Markdown requirements document.
|
|
94
|
+
*/
|
|
95
|
+
export function buildRequirementsMarkdown(metadata, requirements, documentTitle) {
|
|
96
|
+
let markdown = buildFrontmatter(metadata);
|
|
97
|
+
markdown += '\n\n';
|
|
98
|
+
// Add document title if provided
|
|
99
|
+
if (documentTitle) {
|
|
100
|
+
markdown += `# ${documentTitle}\n\n`;
|
|
101
|
+
}
|
|
102
|
+
// Add each requirement section
|
|
103
|
+
for (const req of requirements) {
|
|
104
|
+
markdown += buildRequirementSection(req);
|
|
105
|
+
markdown += '\n';
|
|
106
|
+
}
|
|
107
|
+
return markdown.trimEnd() + '\n';
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Build Markdown for a single requirement (for testing/debugging).
|
|
111
|
+
*/
|
|
112
|
+
export function buildRequirementMarkdown(node) {
|
|
113
|
+
return buildRequirementSection(node);
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Build a complete requirements file from metadata and raw markdown content.
|
|
117
|
+
* Used by CLI pull to combine frontmatter with cloud-sourced markdown.
|
|
118
|
+
*
|
|
119
|
+
* SYNC-ARCH-1: markdownContent is the source of truth from the cloud.
|
|
120
|
+
*/
|
|
121
|
+
export function buildRequirementsFile(metadata, markdownContent) {
|
|
122
|
+
const frontmatter = buildFrontmatter(metadata);
|
|
123
|
+
return `${frontmatter}\n\n${markdownContent.trim()}\n`;
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=builder.js.map
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert between Convex flat representation and hierarchical RequirementNode trees.
|
|
3
|
+
*/
|
|
4
|
+
import { RequirementNode, Metadata } from './schemas.js';
|
|
5
|
+
/**
|
|
6
|
+
* Convex requirement type (flat structure with position paths).
|
|
7
|
+
* This mirrors the Convex database schema.
|
|
8
|
+
*/
|
|
9
|
+
export interface ConvexRequirement {
|
|
10
|
+
_id: string;
|
|
11
|
+
prefix: string;
|
|
12
|
+
index: number;
|
|
13
|
+
documentId: string;
|
|
14
|
+
label: string;
|
|
15
|
+
content: string;
|
|
16
|
+
projectId: string;
|
|
17
|
+
rootId?: string;
|
|
18
|
+
position?: string;
|
|
19
|
+
metadata?: any;
|
|
20
|
+
externalLinks?: {
|
|
21
|
+
jira?: string;
|
|
22
|
+
notion?: string;
|
|
23
|
+
};
|
|
24
|
+
updatedAt: number;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Construct a requirement key from prefix and index.
|
|
28
|
+
* E.g., ("REQ", 123) -> "REQ-123"
|
|
29
|
+
*/
|
|
30
|
+
export declare function constructKey(prefix: string, index: number): string;
|
|
31
|
+
/**
|
|
32
|
+
* Parse a requirement key into prefix and index.
|
|
33
|
+
* E.g., "REQ-123" -> { prefix: "REQ", index: 123 }
|
|
34
|
+
* Returns null if key doesn't match expected format.
|
|
35
|
+
*/
|
|
36
|
+
export declare function parseKey(key: string): {
|
|
37
|
+
prefix: string;
|
|
38
|
+
index: number;
|
|
39
|
+
} | null;
|
|
40
|
+
/**
|
|
41
|
+
* Build a hierarchical tree from flat Convex requirements.
|
|
42
|
+
* Uses position paths to reconstruct parent-child relationships.
|
|
43
|
+
*/
|
|
44
|
+
export declare function convexToRequirements(convexReqs: ConvexRequirement[]): RequirementNode[];
|
|
45
|
+
/**
|
|
46
|
+
* Flatten a hierarchical structure back to Convex flat format.
|
|
47
|
+
* Generates position paths for each node.
|
|
48
|
+
*/
|
|
49
|
+
export declare function requirementsToConvex(nodes: RequirementNode[], projectId: string, documentId: string): ConvexRequirement[];
|
|
50
|
+
/**
|
|
51
|
+
* Build metadata from project info and pull time.
|
|
52
|
+
* DOC-HEADER-11.3: When pulling, include defaultPrefix in the frontmatter.
|
|
53
|
+
*/
|
|
54
|
+
export declare function buildMetadata(projectId: string, version?: number, document?: {
|
|
55
|
+
id?: string;
|
|
56
|
+
title: string;
|
|
57
|
+
defaultPrefix?: string;
|
|
58
|
+
}): Metadata;
|
|
59
|
+
/**
|
|
60
|
+
* Extract unique requirement keys from Convex data.
|
|
61
|
+
* Useful for detecting duplicates or tracking which requirements exist.
|
|
62
|
+
*/
|
|
63
|
+
export declare function extractRequirementKeys(convexReqs: ConvexRequirement[]): Set<string>;
|
|
64
|
+
/**
|
|
65
|
+
* Group Convex requirements by their root.
|
|
66
|
+
* Returns a map of rootId → children.
|
|
67
|
+
*/
|
|
68
|
+
export declare function groupByRoot(convexReqs: ConvexRequirement[]): Map<string | undefined, ConvexRequirement[]>;
|
|
69
|
+
//# sourceMappingURL=conversions.d.ts.map
|