@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,45 @@
1
+ /**
2
+ * Requirements loader for test harness.
3
+ * Loads requirements from .requirements/ directories using shared schema parsing.
4
+ *
5
+ * Project root discovery (in priority order):
6
+ * 1. Explicit projectRoot option passed to loadRequirements()
7
+ * 2. DOTREQUIREMENTS_PROJECT_ROOT environment variable
8
+ * 3. Cached project root file (.requirements/.cache/project-root) written by prepare()
9
+ */
10
+ import { type RequirementNode } from '../schema/index.js';
11
+ import { Requirement } from './types.js';
12
+ export declare const loadedRequirements: Map<string, RequirementNode>;
13
+ export interface LoadOptions {
14
+ /** Explicit project root path. Takes precedence over env var and cached file. */
15
+ projectRoot?: string;
16
+ }
17
+ /**
18
+ * Load all requirements from .requirements/ directories.
19
+ *
20
+ * Tries lookup cache first (fast path after prepare() was called).
21
+ * Falls back to parsing .requirements.md files on-demand.
22
+ *
23
+ * @param options.projectRoot - Explicit project root path (optional)
24
+ */
25
+ export declare function loadRequirements(options?: LoadOptions): void;
26
+ /**
27
+ * Get a requirement by ID (supports both numeric and label-based paths).
28
+ *
29
+ * Examples:
30
+ * - getRequirement('REQ-123') - Root requirement
31
+ * - getRequirement('REQ-123.0') - First child (numeric)
32
+ * - getRequirement('REQ-123.given') - First "given" child (label)
33
+ * - getRequirement('REQ-123.given#1') - Second "given" child (disambiguated)
34
+ * - getRequirement('REQ-123.0.1') - Nested child
35
+ */
36
+ export declare function getRequirement(reqId: string): Requirement | undefined;
37
+ /**
38
+ * Get all loaded requirements (flat map)
39
+ */
40
+ export declare function getAllRequirements(): Map<string, Requirement>;
41
+ /**
42
+ * Get all root requirement trees
43
+ */
44
+ export declare function getAllRequirementTrees(): Map<string, Requirement>;
45
+ //# sourceMappingURL=requirementsLoader.d.ts.map
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Requirements loader for test harness.
3
+ * Loads requirements from .requirements/ directories using shared schema parsing.
4
+ *
5
+ * Project root discovery (in priority order):
6
+ * 1. Explicit projectRoot option passed to loadRequirements()
7
+ * 2. DOTREQUIREMENTS_PROJECT_ROOT environment variable
8
+ * 3. Cached project root file (.requirements/.cache/project-root) written by prepare()
9
+ */
10
+ import * as fs from 'fs';
11
+ import * as path from 'path';
12
+ import { globSync } from 'glob';
13
+ import { parseRequirementsFromFile, resolveRequirementPath, } from '../schema/index.js';
14
+ import { findCachedProjectRoot, readLookupCache, } from './cache.js';
15
+ // Cache: Maps full requirement IDs (e.g., "REQ-123.0.1") to RequirementNode
16
+ export const loadedRequirements = new Map();
17
+ // Cache: Maps root requirement IDs (e.g., "REQ-123") to their tree
18
+ const requirementTrees = new Map();
19
+ // Flag to track if we've warned about fallback parsing
20
+ let warnedAboutFallback = false;
21
+ /**
22
+ * Find the project root using the priority order:
23
+ * 1. Explicit projectRoot option
24
+ * 2. DOTREQUIREMENTS_PROJECT_ROOT env var
25
+ * 3. Cached project root file
26
+ */
27
+ function findProjectRoot(options = {}) {
28
+ // Priority 1: Explicit option
29
+ if (options.projectRoot) {
30
+ return options.projectRoot;
31
+ }
32
+ // Priority 2: Environment variable
33
+ if (process.env.DOTREQUIREMENTS_PROJECT_ROOT) {
34
+ return process.env.DOTREQUIREMENTS_PROJECT_ROOT;
35
+ }
36
+ // Priority 3: Cached file (written by prepare())
37
+ return findCachedProjectRoot(process.cwd());
38
+ }
39
+ /**
40
+ * Find all *.requirements.md files under project root synchronously.
41
+ */
42
+ function findRequirementsFilesSync(projectRoot) {
43
+ const pattern = '**/*.requirements.md';
44
+ const matches = globSync(pattern, {
45
+ cwd: projectRoot,
46
+ ignore: [
47
+ '**/node_modules/**',
48
+ '**/dist/**',
49
+ '**/.git/**',
50
+ '**/build/**',
51
+ '**/example/**',
52
+ '**/examples/**',
53
+ '**/__tests__/**',
54
+ '**/fixtures/**',
55
+ '**/.fixtures/**',
56
+ ],
57
+ dot: true, // Include dotfiles/dotdirs like .requirements/
58
+ });
59
+ return matches.map(m => path.join(projectRoot, m));
60
+ }
61
+ /**
62
+ * Try to load requirements from lookup cache (fast path).
63
+ * Returns true if cache was loaded, false if not available.
64
+ */
65
+ function tryLoadFromCache(projectRoot) {
66
+ const requirementsDir = path.join(projectRoot, '.requirements');
67
+ if (!fs.existsSync(requirementsDir)) {
68
+ return false;
69
+ }
70
+ const cache = readLookupCache(requirementsDir);
71
+ if (!cache) {
72
+ return false;
73
+ }
74
+ // Load all requirements from cache into the flat map
75
+ for (const [id, req] of Object.entries(cache.requirements)) {
76
+ // Create a minimal RequirementNode structure
77
+ const node = {
78
+ id: req.id,
79
+ label: req.label,
80
+ content: req.content,
81
+ children: [], // Children are loaded separately by ID
82
+ };
83
+ loadedRequirements.set(id, node);
84
+ // If this is a root node (no dot in ID), add to trees cache
85
+ if (!id.includes('.')) {
86
+ requirementTrees.set(id, node);
87
+ }
88
+ }
89
+ return true;
90
+ }
91
+ /**
92
+ * Recursively flatten a requirement tree into the cache.
93
+ * Adds both the root and all children with their full IDs.
94
+ */
95
+ function flattenRequirementTree(node) {
96
+ // Add this node to the flat cache
97
+ loadedRequirements.set(node.id, node);
98
+ // If this is a root node (no dot in ID), also add to trees cache
99
+ if (!node.id.includes('.')) {
100
+ requirementTrees.set(node.id, node);
101
+ }
102
+ // Recursively add children
103
+ for (const child of node.children) {
104
+ flattenRequirementTree(child);
105
+ }
106
+ }
107
+ /**
108
+ * Load all requirements from .requirements/ directories.
109
+ *
110
+ * Tries lookup cache first (fast path after prepare() was called).
111
+ * Falls back to parsing .requirements.md files on-demand.
112
+ *
113
+ * @param options.projectRoot - Explicit project root path (optional)
114
+ */
115
+ export function loadRequirements(options = {}) {
116
+ // Clear existing cache when loading
117
+ loadedRequirements.clear();
118
+ requirementTrees.clear();
119
+ // Find project root
120
+ const projectRoot = findProjectRoot(options);
121
+ if (!projectRoot) {
122
+ throw new Error('Could not find project root. ' +
123
+ 'Set DOTREQUIREMENTS_PROJECT_ROOT environment variable or run prepare() in globalSetup.');
124
+ }
125
+ // Try cache first (fast path)
126
+ if (tryLoadFromCache(projectRoot)) {
127
+ return;
128
+ }
129
+ // Fallback: Parse .requirements.md files on-demand
130
+ if (!warnedAboutFallback && !options.projectRoot) {
131
+ console.warn('⚠️ Performance warning: prepare() was not called. ' +
132
+ 'Parsing requirements on-demand. Run prepare() in globalSetup for faster tests.');
133
+ warnedAboutFallback = true;
134
+ }
135
+ // Find all .requirements.md files under the project root
136
+ const requirementsFiles = findRequirementsFilesSync(projectRoot);
137
+ // Parse and flatten each file
138
+ for (const file of requirementsFiles) {
139
+ try {
140
+ const { requirements } = parseRequirementsFromFile(file);
141
+ // Flatten each requirement tree into the cache
142
+ for (const req of requirements) {
143
+ flattenRequirementTree(req);
144
+ }
145
+ }
146
+ catch (error) {
147
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
148
+ console.error(`Error loading ${file}:`, errorMessage);
149
+ }
150
+ }
151
+ }
152
+ /**
153
+ * Get a requirement by ID (supports both numeric and label-based paths).
154
+ *
155
+ * Examples:
156
+ * - getRequirement('REQ-123') - Root requirement
157
+ * - getRequirement('REQ-123.0') - First child (numeric)
158
+ * - getRequirement('REQ-123.given') - First "given" child (label)
159
+ * - getRequirement('REQ-123.given#1') - Second "given" child (disambiguated)
160
+ * - getRequirement('REQ-123.0.1') - Nested child
161
+ */
162
+ export function getRequirement(reqId) {
163
+ if (loadedRequirements.size === 0) {
164
+ loadRequirements();
165
+ }
166
+ // First try direct lookup (for numeric paths like "REQ-123.0.1")
167
+ const direct = loadedRequirements.get(reqId);
168
+ if (direct) {
169
+ return direct;
170
+ }
171
+ // If not found, try resolving label-based paths
172
+ // resolveRequirementPath expects an array of root requirements and the full path
173
+ const allTrees = Array.from(requirementTrees.values());
174
+ try {
175
+ const resolved = resolveRequirementPath(allTrees, reqId);
176
+ return resolved ?? undefined;
177
+ }
178
+ catch {
179
+ // Path resolution failed
180
+ return undefined;
181
+ }
182
+ }
183
+ /**
184
+ * Get all loaded requirements (flat map)
185
+ */
186
+ export function getAllRequirements() {
187
+ if (loadedRequirements.size === 0) {
188
+ loadRequirements();
189
+ }
190
+ return loadedRequirements;
191
+ }
192
+ /**
193
+ * Get all root requirement trees
194
+ */
195
+ export function getAllRequirementTrees() {
196
+ if (requirementTrees.size === 0) {
197
+ loadRequirements();
198
+ }
199
+ return requirementTrees;
200
+ }
201
+ //# sourceMappingURL=requirementsLoader.js.map
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Tracking for the test harness.
3
+ *
4
+ * Tracks requirement() calls during test execution.
5
+ * Uses JSONL format for cross-process safety (parallel test workers).
6
+ *
7
+ * HARNESS-REQUIREMENT-3: Each requirement() call is recorded for coverage reporting
8
+ */
9
+ export interface TrackedRequirement {
10
+ id: string;
11
+ fieldsAccessed: string[];
12
+ accessedAt: string[];
13
+ }
14
+ export declare function ensureTestRun(): string;
15
+ export declare function getCallerLocation(): string;
16
+ /**
17
+ * Track a requirement access.
18
+ *
19
+ * HARNESS-REQUIREMENT-3.0: After a test calls requirement(path), finalize() can report
20
+ * HARNESS-REQUIREMENT-3.1: Coverage record includes which file and line called requirement()
21
+ * HARNESS-REQUIREMENT-3.2: Works correctly when tests run in parallel processes
22
+ *
23
+ * @param reqId - Full requirement path (e.g., 'REQ-123.given' or 'REQ-123.0.1')
24
+ */
25
+ export declare function trackRequirement(reqId: string): void;
26
+ /**
27
+ * Save current tracking data.
28
+ *
29
+ * In the new JSONL-based approach, this is a no-op since tracking is
30
+ * written immediately via appendTrackingEntry. Kept for backward compatibility.
31
+ */
32
+ export declare function saveTrackingData(): void;
33
+ export declare function getTrackedRequirements(): Map<string, TrackedRequirement>;
34
+ export declare function clearTracking(): void;
35
+ /**
36
+ * Initialize a test run (called by globalSetup).
37
+ *
38
+ * @deprecated Use prepare() from './prepare.js' instead.
39
+ * This function is kept for backward compatibility.
40
+ */
41
+ export declare function initTestRun(): void;
42
+ /**
43
+ * Finalize test run and generate report (called by globalTeardown).
44
+ *
45
+ * @deprecated Use finalize() from './finalize.js' instead.
46
+ * This function is kept for backward compatibility.
47
+ */
48
+ export declare function finalizeTestRun(): void;
49
+ //# sourceMappingURL=tracking.d.ts.map
@@ -0,0 +1,179 @@
1
+ /**
2
+ * Tracking for the test harness.
3
+ *
4
+ * Tracks requirement() calls during test execution.
5
+ * Uses JSONL format for cross-process safety (parallel test workers).
6
+ *
7
+ * HARNESS-REQUIREMENT-3: Each requirement() call is recorded for coverage reporting
8
+ */
9
+ import * as path from 'path';
10
+ import { findRequirementsDir, findProjectRoot, getTestRunId, appendTrackingEntry, getCacheDir, initTestRunId, clearTrackingFile, } from './cache.js';
11
+ const trackedRequirements = new Map();
12
+ // Test run tracking
13
+ let testRunId = null;
14
+ let requirementsDirCached = null;
15
+ // Initialize test run
16
+ export function ensureTestRun() {
17
+ if (!testRunId) {
18
+ // Priority 1: Use environment variable (cross-process persistence from globalSetup)
19
+ if (process.env.DOTREQUIREMENTS_PROJECT_ROOT) {
20
+ requirementsDirCached = path.join(process.env.DOTREQUIREMENTS_PROJECT_ROOT, '.requirements');
21
+ }
22
+ // Priority 2: Find from caller directory
23
+ if (!requirementsDirCached) {
24
+ const callerDir = getCallerDirectory();
25
+ requirementsDirCached = findRequirementsDir(callerDir || process.cwd());
26
+ // If not found from caller directory, try from cwd explicitly
27
+ if (!requirementsDirCached && callerDir) {
28
+ requirementsDirCached = findRequirementsDir(process.cwd());
29
+ }
30
+ }
31
+ if (!requirementsDirCached) {
32
+ throw new Error('Could not find .requirements directory. Please create one in your project root.');
33
+ }
34
+ // Try to read from shared test run ID file (set by prepare())
35
+ const existingRunId = getTestRunId(requirementsDirCached);
36
+ if (existingRunId) {
37
+ testRunId = existingRunId;
38
+ }
39
+ else {
40
+ // Fallback for standalone usage without prepare()
41
+ testRunId = `${Date.now()}-${process.pid}`;
42
+ }
43
+ // Ensure cache directory exists
44
+ getCacheDir(requirementsDirCached, true);
45
+ }
46
+ return testRunId;
47
+ }
48
+ // Get caller location for tracking
49
+ export function getCallerLocation() {
50
+ const error = new Error();
51
+ const stack = error.stack?.split('\n') || [];
52
+ // Find the first stack frame that's not in our library files
53
+ for (let i = 2; i < stack.length; i++) {
54
+ const frame = stack[i];
55
+ if (!frame.includes('/harness/index.ts') &&
56
+ !frame.includes('/harness/index.js') &&
57
+ !frame.includes('/harness/tracking.ts') &&
58
+ !frame.includes('/harness/tracking.js')) {
59
+ // Extract filename and line number
60
+ const regex1 = /\((.+):(\d+):(\d+)\)/;
61
+ const regex2 = /at (.+):(\d+):(\d+)/;
62
+ const match = regex1.exec(frame) || regex2.exec(frame);
63
+ if (match) {
64
+ const filePath = match[1];
65
+ const lineNumber = match[2];
66
+ return `${path.basename(filePath)}:${lineNumber}`;
67
+ }
68
+ }
69
+ }
70
+ return 'unknown';
71
+ }
72
+ // Get caller's directory (full path) for finding .requirements
73
+ function getCallerDirectory() {
74
+ const error = new Error();
75
+ const stack = error.stack?.split('\n') || [];
76
+ // Find the first stack frame that's not in our library files
77
+ for (let i = 2; i < stack.length; i++) {
78
+ const frame = stack[i];
79
+ if (!frame.includes('/harness/index.ts') &&
80
+ !frame.includes('/harness/index.js') &&
81
+ !frame.includes('/harness/tracking.ts') &&
82
+ !frame.includes('/harness/tracking.js')) {
83
+ // Extract filename
84
+ const regex1 = /\((.+):(\d+):(\d+)\)/;
85
+ const regex2 = /at (.+):(\d+):(\d+)/;
86
+ const match = regex1.exec(frame) || regex2.exec(frame);
87
+ if (match) {
88
+ const filePath = match[1];
89
+ return path.dirname(filePath);
90
+ }
91
+ }
92
+ }
93
+ return null;
94
+ }
95
+ /**
96
+ * Track a requirement access.
97
+ *
98
+ * HARNESS-REQUIREMENT-3.0: After a test calls requirement(path), finalize() can report
99
+ * HARNESS-REQUIREMENT-3.1: Coverage record includes which file and line called requirement()
100
+ * HARNESS-REQUIREMENT-3.2: Works correctly when tests run in parallel processes
101
+ *
102
+ * @param reqId - Full requirement path (e.g., 'REQ-123.given' or 'REQ-123.0.1')
103
+ */
104
+ export function trackRequirement(reqId) {
105
+ const callerLocation = getCallerLocation();
106
+ const runId = ensureTestRun();
107
+ // Track in memory for backward compatibility
108
+ if (!trackedRequirements.has(reqId)) {
109
+ trackedRequirements.set(reqId, {
110
+ id: reqId,
111
+ fieldsAccessed: [], // Kept for backward compatibility
112
+ accessedAt: [],
113
+ });
114
+ }
115
+ const tracked = trackedRequirements.get(reqId);
116
+ tracked.accessedAt.push(callerLocation);
117
+ // Append to JSONL file for cross-process tracking
118
+ // HARNESS-REQUIREMENT-3.2: JSONL is append-only, safe for parallel workers
119
+ if (requirementsDirCached) {
120
+ const entry = {
121
+ requirementKey: reqId,
122
+ callerLocation,
123
+ timestamp: Date.now(),
124
+ testRunId: runId,
125
+ };
126
+ appendTrackingEntry(requirementsDirCached, entry);
127
+ }
128
+ }
129
+ /**
130
+ * Save current tracking data.
131
+ *
132
+ * In the new JSONL-based approach, this is a no-op since tracking is
133
+ * written immediately via appendTrackingEntry. Kept for backward compatibility.
134
+ */
135
+ export function saveTrackingData() {
136
+ // No-op: JSONL entries are written immediately in trackRequirement()
137
+ }
138
+ // Get all tracked requirements (in-memory)
139
+ export function getTrackedRequirements() {
140
+ return trackedRequirements;
141
+ }
142
+ // Clear tracking (for testing)
143
+ export function clearTracking() {
144
+ trackedRequirements.clear();
145
+ testRunId = null;
146
+ requirementsDirCached = null;
147
+ }
148
+ /**
149
+ * Initialize a test run (called by globalSetup).
150
+ *
151
+ * @deprecated Use prepare() from './prepare.js' instead.
152
+ * This function is kept for backward compatibility.
153
+ */
154
+ export function initTestRun() {
155
+ const projectRoot = findProjectRoot();
156
+ if (!projectRoot) {
157
+ throw new Error('Could not find .requirements directory. Please create one in your project root.');
158
+ }
159
+ const reqDir = findRequirementsDir();
160
+ // Initialize test run ID
161
+ initTestRunId(reqDir);
162
+ // Clear previous tracking data
163
+ clearTrackingFile(reqDir);
164
+ }
165
+ /**
166
+ * Finalize test run and generate report (called by globalTeardown).
167
+ *
168
+ * @deprecated Use finalize() from './finalize.js' instead.
169
+ * This function is kept for backward compatibility.
170
+ */
171
+ export function finalizeTestRun() {
172
+ // Import and call the new finalize function
173
+ // Using dynamic import to avoid circular dependencies
174
+ const { finalize } = require('./finalize.js');
175
+ finalize().catch((error) => {
176
+ console.error('Error in finalizeTestRun:', error.message);
177
+ });
178
+ }
179
+ //# sourceMappingURL=tracking.js.map
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Type definitions for test harness.
3
+ * Uses shared schema types for consistency with CLI and MCP.
4
+ */
5
+ import type { RequirementNode } from '../schema/index.js';
6
+ export type { RequirementNode as Requirement };
7
+ export interface CriteriaDetail {
8
+ reqId: string;
9
+ totalCriteria: number;
10
+ testedCriteria: number;
11
+ }
12
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Type definitions for test harness.
3
+ * Uses shared schema types for consistency with CLI and MCP.
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Load Convex configuration from .env file or environment variables
3
+ */
4
+ interface ConvexConfig {
5
+ convexUrl: string;
6
+ projectId: string;
7
+ projectSecret: string;
8
+ }
9
+ export declare function loadConvexConfig(cwd?: string): ConvexConfig | null;
10
+ /**
11
+ * Query Convex for requirement coverage
12
+ */
13
+ export declare function getRequirementCoverage(requirementKey: string, projectId: string, projectSecret: string, convexUrl: string): Promise<{
14
+ requirementKey: string;
15
+ lastTestedAt: number | null;
16
+ branch: string | null;
17
+ testFile: string | null;
18
+ testLine: number | null;
19
+ allBranches: Array<{
20
+ branch: string;
21
+ lastTestedAt: number;
22
+ testFile?: string;
23
+ testLine?: number;
24
+ }>;
25
+ }>;
26
+ /**
27
+ * Query Convex for project coverage summary
28
+ */
29
+ export declare function getProjectCoverage(projectId: string, projectSecret: string, convexUrl: string, options?: {
30
+ branch?: string;
31
+ sinceTimestamp?: number;
32
+ }): Promise<{
33
+ tested: Array<{
34
+ requirementKey: string;
35
+ lastTestedAt: number;
36
+ branch: string;
37
+ testFile?: string;
38
+ testLine?: number;
39
+ }>;
40
+ untested: string[];
41
+ }>;
42
+ export {};
43
+ //# sourceMappingURL=convexClient.d.ts.map
@@ -0,0 +1,101 @@
1
+ import { readFileSync, existsSync } from 'fs';
2
+ import { join } from 'path';
3
+ /**
4
+ * Production Convex deployment URL
5
+ */
6
+ const CONVEX_URL = 'https://data.dotrequirements.io';
7
+ export function loadConvexConfig(cwd = process.cwd()) {
8
+ let projectId;
9
+ let projectSecret;
10
+ // Try to load from .env files (.env.local takes precedence over .env)
11
+ const envFiles = ['.env.local', '.env'];
12
+ for (const envFile of envFiles) {
13
+ const envPath = join(cwd, envFile);
14
+ if (existsSync(envPath)) {
15
+ const envContent = readFileSync(envPath, 'utf-8');
16
+ const lines = envContent.split('\n');
17
+ for (const line of lines) {
18
+ const trimmed = line.trim();
19
+ if (trimmed.startsWith('DOTREQUIREMENTS_PROJECT_ID=')) {
20
+ projectId = trimmed.split('=')[1].trim().replace(/['"]/g, '');
21
+ }
22
+ if (trimmed.startsWith('DOTREQUIREMENTS_PROJECT_SECRET=')) {
23
+ projectSecret = trimmed.split('=')[1].trim().replace(/['"]/g, '');
24
+ }
25
+ }
26
+ // If we found values, stop looking (precedence: .env.local > .env)
27
+ if (projectId && projectSecret) {
28
+ break;
29
+ }
30
+ }
31
+ }
32
+ // Environment variables take precedence
33
+ if (process.env.DOTREQUIREMENTS_PROJECT_ID) {
34
+ projectId = process.env.DOTREQUIREMENTS_PROJECT_ID;
35
+ }
36
+ if (process.env.DOTREQUIREMENTS_PROJECT_SECRET) {
37
+ projectSecret = process.env.DOTREQUIREMENTS_PROJECT_SECRET;
38
+ }
39
+ if (!projectId || !projectSecret) {
40
+ return null;
41
+ }
42
+ return { convexUrl: CONVEX_URL, projectId, projectSecret };
43
+ }
44
+ /**
45
+ * Query Convex for requirement coverage
46
+ */
47
+ export async function getRequirementCoverage(requirementKey, projectId, projectSecret, convexUrl) {
48
+ const response = await fetch(`${convexUrl}/api/query`, {
49
+ method: 'POST',
50
+ headers: { 'Content-Type': 'application/json' },
51
+ body: JSON.stringify({
52
+ path: 'testCoverage/queries:getRequirementCoverage',
53
+ args: {
54
+ projectAuth: {
55
+ projectSlug: projectId,
56
+ projectSecret,
57
+ },
58
+ target: {
59
+ type: 'project',
60
+ slug: projectId,
61
+ },
62
+ requirementKey,
63
+ },
64
+ format: 'json',
65
+ }),
66
+ });
67
+ if (!response.ok) {
68
+ throw new Error(`Failed to query coverage: ${response.statusText}`);
69
+ }
70
+ return await response.json();
71
+ }
72
+ /**
73
+ * Query Convex for project coverage summary
74
+ */
75
+ export async function getProjectCoverage(projectId, projectSecret, convexUrl, options) {
76
+ const response = await fetch(`${convexUrl}/api/query`, {
77
+ method: 'POST',
78
+ headers: { 'Content-Type': 'application/json' },
79
+ body: JSON.stringify({
80
+ path: 'testCoverage/queries:getProjectCoverage',
81
+ args: {
82
+ projectAuth: {
83
+ projectSlug: projectId,
84
+ projectSecret,
85
+ },
86
+ target: {
87
+ type: 'project',
88
+ slug: projectId,
89
+ },
90
+ branch: options?.branch,
91
+ sinceTimestamp: options?.sinceTimestamp,
92
+ },
93
+ format: 'json',
94
+ }),
95
+ });
96
+ if (!response.ok) {
97
+ throw new Error(`Failed to query coverage: ${response.statusText}`);
98
+ }
99
+ return await response.json();
100
+ }
101
+ //# sourceMappingURL=convexClient.js.map
@@ -0,0 +1,24 @@
1
+ import type { TestReference } from './types.js';
2
+ /**
3
+ * Find all test files that reference a specific requirement ID
4
+ *
5
+ * Uses execFile with array arguments to avoid command injection vulnerabilities.
6
+ */
7
+ export declare function findTestsForRequirement(workspaceRoot: string, requirementId: string): Promise<TestReference[]>;
8
+ /**
9
+ * Find all requirement references in a specific file using AST parsing
10
+ */
11
+ export declare function findRequirementsInFile(filePath: string): Promise<TestReference[]>;
12
+ /**
13
+ * Find all requirement references in the entire workspace
14
+ *
15
+ * Uses execFile with array arguments to avoid command injection vulnerabilities.
16
+ */
17
+ export declare function findAllTestReferences(workspaceRoot: string): Promise<TestReference[]>;
18
+ /**
19
+ * Get all unique requirement IDs referenced in tests
20
+ * Normalizes child IDs (e.g., AUTH-VALID-LOGIN.0) to root IDs (e.g., AUTH-VALID-LOGIN)
21
+ * Uses AST parsing instead of grep for reliability across environments
22
+ */
23
+ export declare function getReferencedRequirementIds(workspaceRoot: string): Promise<Set<string>>;
24
+ //# sourceMappingURL=grep.d.ts.map