@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,310 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import { ConvexHttpClient } from 'convex/browser';
4
+ import { getConvexUrl } from '../config.js';
5
+ import { parseRequirementsFromFile, getAllRequirements, parseRequirementKey, buildRequirementsFile } from '../schema/index.js';
6
+ import { api } from '../convex.js';
7
+ import * as readline from 'readline';
8
+ import { isLocalOnlyProject, CLOUD_FEATURES_REQUIRE_AUTH_MESSAGE } from '../utils/local-project.js';
9
+ import { brand } from '../utils/brand.js';
10
+ export async function pushCommand(file, options) {
11
+ try {
12
+ console.log(`Preparing to push requirements to ${brand} cloud...\n`);
13
+ // Verify project credentials are configured
14
+ const projectId = process.env.DOTREQUIREMENTS_PROJECT_ID;
15
+ const projectSecret = process.env.DOTREQUIREMENTS_PROJECT_SECRET;
16
+ if (!projectId || !projectSecret) {
17
+ throw new Error('Project credentials not found. Either:\n' +
18
+ ' 1. Run "dotreq init" to initialize the project, or\n' +
19
+ ' 2. Set DOTREQUIREMENTS_PROJECT_ID and DOTREQUIREMENTS_PROJECT_SECRET in .env.local');
20
+ }
21
+ // AUTHZ-2.0: Check if project is local-only
22
+ if (isLocalOnlyProject(projectId)) {
23
+ throw new Error(CLOUD_FEATURES_REQUIRE_AUTH_MESSAGE);
24
+ }
25
+ // Determine which files to push
26
+ const requirementsDir = path.join(process.cwd(), '.requirements');
27
+ if (!fs.existsSync(requirementsDir)) {
28
+ console.log('No .requirements/ directory found. Nothing to push.');
29
+ return;
30
+ }
31
+ let filesToPush;
32
+ if (file) {
33
+ // Push specific file
34
+ const filePath = path.resolve(file);
35
+ if (!fs.existsSync(filePath)) {
36
+ throw new Error(`File not found: ${file}`);
37
+ }
38
+ filesToPush = [filePath];
39
+ }
40
+ else {
41
+ // Push all *.requirements.md files in .requirements/
42
+ const files = fs.readdirSync(requirementsDir);
43
+ filesToPush = files
44
+ .filter(f => f.endsWith('.requirements.md'))
45
+ .map(f => path.join(requirementsDir, f));
46
+ if (filesToPush.length === 0) {
47
+ console.log('No *.requirements.md files found in .requirements/. Nothing to push.');
48
+ return;
49
+ }
50
+ }
51
+ // Parse all local files
52
+ console.log('Parsing local requirements...');
53
+ const parsedFiles = [];
54
+ let totalRequirements = 0;
55
+ for (const filePath of filesToPush) {
56
+ const fileName = path.basename(filePath);
57
+ console.log(` Reading ${fileName}...`);
58
+ // Read raw file content
59
+ const rawContent = fs.readFileSync(filePath, 'utf-8');
60
+ // Parse to get metadata and requirements
61
+ const parsed = parseRequirementsFromFile(filePath);
62
+ const flatRequirements = getAllRequirements(parsed.requirements);
63
+ // Extract markdown content (everything after frontmatter)
64
+ const markdownContent = extractMarkdownContent(rawContent);
65
+ // DOC-HEADER-11.1/11.2: Infer prefix from first requirement if not specified
66
+ const doc = parsed.metadata.document;
67
+ if (doc && !doc.defaultPrefix && flatRequirements.length > 0) {
68
+ const firstReq = flatRequirements[0];
69
+ if (firstReq) {
70
+ const parsedKey = parseRequirementKey(firstReq.id);
71
+ if (parsedKey) {
72
+ doc.defaultPrefix = parsedKey.prefix;
73
+ console.log(` Inferred defaultPrefix "${doc.defaultPrefix}" from first requirement`);
74
+ }
75
+ }
76
+ }
77
+ parsedFiles.push({
78
+ filePath,
79
+ metadata: parsed.metadata,
80
+ markdownContent,
81
+ requirementCount: flatRequirements.length,
82
+ });
83
+ totalRequirements += flatRequirements.length;
84
+ }
85
+ console.log(`\nFound ${totalRequirements} requirement(s) in ${parsedFiles.length} document(s).\n`);
86
+ // Connect to Convex
87
+ const convexUrl = getConvexUrl();
88
+ const client = new ConvexHttpClient(convexUrl);
89
+ // Phase 1: Dry run to categorize all files
90
+ console.log('Validating documents...');
91
+ const dryRunResults = [];
92
+ for (const file of parsedFiles) {
93
+ const doc = file.metadata.document;
94
+ const fileName = path.basename(file.filePath);
95
+ if (!doc) {
96
+ // No document section - mark as invalid locally
97
+ dryRunResults.push({
98
+ file,
99
+ result: {
100
+ dryRun: true,
101
+ action: 'invalid',
102
+ title: fileName,
103
+ error: 'Missing document section in frontmatter',
104
+ },
105
+ });
106
+ continue;
107
+ }
108
+ try {
109
+ const result = await client.mutation(api.documents.saveWithRequirements.saveWithRequirements, {
110
+ projectAuth: {
111
+ projectSlug: projectId,
112
+ projectSecret,
113
+ },
114
+ target: { type: 'project', slug: projectId },
115
+ documentId: doc.id,
116
+ title: doc.title,
117
+ markdownContent: file.markdownContent,
118
+ defaultPrefix: doc.defaultPrefix,
119
+ dryRun: true,
120
+ });
121
+ dryRunResults.push({ file, result });
122
+ }
123
+ catch (err) {
124
+ dryRunResults.push({
125
+ file,
126
+ result: {
127
+ dryRun: true,
128
+ action: 'invalid',
129
+ title: doc.title,
130
+ error: err.message,
131
+ },
132
+ });
133
+ }
134
+ }
135
+ // Categorize results
136
+ const updates = dryRunResults.filter(r => r.result.action === 'update');
137
+ const creates = dryRunResults.filter(r => r.result.action === 'create');
138
+ const notFound = dryRunResults.filter(r => r.result.action === 'not_found');
139
+ const invalid = dryRunResults.filter(r => r.result.action === 'invalid');
140
+ // Check for conflicts on updates (cloud changed since last pull)
141
+ const conflicts = [];
142
+ if (updates.length > 0) {
143
+ const docIds = updates.map(u => u.result.documentId);
144
+ const cloudMetadata = await client.query(api.documents.queries.getDocumentsMetadata, {
145
+ projectAuth: {
146
+ projectSlug: projectId,
147
+ projectSecret,
148
+ },
149
+ target: { type: 'project', slug: projectId },
150
+ documentIds: docIds,
151
+ });
152
+ const cloudMetaByDocId = new Map(cloudMetadata.map((m) => [m.documentId, m]));
153
+ for (const item of updates) {
154
+ const docId = item.result.documentId;
155
+ const cloudMeta = cloudMetaByDocId.get(docId);
156
+ if (cloudMeta && item.file.metadata.pulledAt) {
157
+ const pulledAtMs = new Date(item.file.metadata.pulledAt).getTime();
158
+ if (cloudMeta.updatedAt > pulledAtMs) {
159
+ conflicts.push({ item, cloudMeta });
160
+ }
161
+ }
162
+ }
163
+ }
164
+ // Display unified summary
165
+ console.log('\n=== Push Summary ===\n');
166
+ if (updates.length > 0) {
167
+ console.log(`Updated documents (${updates.length}):`);
168
+ for (const { file, result } of updates) {
169
+ const fileName = path.basename(file.filePath);
170
+ const hasConflict = conflicts.some(c => c.item.file === file);
171
+ const conflictNote = hasConflict ? ' ⚠️ [cloud changed since pull]' : '';
172
+ const warningNote = result.warning ? ` (${result.warning})` : '';
173
+ console.log(` ~ ${fileName}${conflictNote}${warningNote}`);
174
+ }
175
+ console.log();
176
+ }
177
+ if (creates.length > 0) {
178
+ console.log(`New documents (${creates.length}):`);
179
+ for (const { file, result } of creates) {
180
+ const fileName = path.basename(file.filePath);
181
+ const warningNote = result.warning ? ` (${result.warning})` : '';
182
+ console.log(` + ${fileName}${warningNote}`);
183
+ }
184
+ console.log();
185
+ }
186
+ if (notFound.length > 0) {
187
+ console.log(`Documents not found in cloud (${notFound.length}) - will create new:`);
188
+ for (const { file, result } of notFound) {
189
+ const fileName = path.basename(file.filePath);
190
+ console.log(` ! ${fileName} (ID: ${result.documentId})`);
191
+ }
192
+ console.log();
193
+ }
194
+ if (invalid.length > 0) {
195
+ console.log(`Skipped - invalid files (${invalid.length}):`);
196
+ for (const { file, result } of invalid) {
197
+ const fileName = path.basename(file.filePath);
198
+ console.log(` ✗ ${fileName}: ${result.error}`);
199
+ }
200
+ console.log();
201
+ }
202
+ // Check if there's anything to push
203
+ const pushableFiles = [...updates, ...creates, ...notFound];
204
+ if (pushableFiles.length === 0) {
205
+ console.log('No valid documents to push.');
206
+ return;
207
+ }
208
+ // Single confirmation prompt
209
+ if (!options.yes) {
210
+ const hasConflicts = conflicts.length > 0;
211
+ const prompt = hasConflicts
212
+ ? 'Continue with push? (will overwrite cloud changes) [y/N] '
213
+ : 'Continue with push? [y/N] ';
214
+ const confirmed = await confirm(prompt);
215
+ if (!confirmed) {
216
+ console.log('Push cancelled.');
217
+ return;
218
+ }
219
+ }
220
+ // Phase 2: Execute push
221
+ console.log('\nPushing documents to cloud...');
222
+ let created = 0;
223
+ let updated = 0;
224
+ for (const { file, result } of pushableFiles) {
225
+ const doc = file.metadata.document;
226
+ const fileName = path.basename(file.filePath);
227
+ // For not_found, clear the ID so we create a new document
228
+ const effectiveDocId = result.action === 'not_found' ? undefined : doc.id;
229
+ try {
230
+ const pushResult = await client.mutation(api.documents.saveWithRequirements.saveWithRequirements, {
231
+ projectAuth: {
232
+ projectSlug: projectId,
233
+ projectSecret,
234
+ },
235
+ target: { type: 'project', slug: projectId },
236
+ documentId: effectiveDocId,
237
+ title: doc.title,
238
+ markdownContent: file.markdownContent,
239
+ defaultPrefix: doc.defaultPrefix,
240
+ dryRun: false,
241
+ });
242
+ const isCreate = result.action === 'create' || result.action === 'not_found';
243
+ if (isCreate) {
244
+ // New document - write ID back to file
245
+ doc.id = pushResult;
246
+ file.metadata.pulledAt = new Date().toISOString();
247
+ file.metadata.version = 1;
248
+ const updatedContent = buildRequirementsFile(file.metadata, file.markdownContent);
249
+ fs.writeFileSync(file.filePath, updatedContent, 'utf-8');
250
+ console.log(` ✓ Created: ${fileName} → ${pushResult}`);
251
+ created++;
252
+ }
253
+ else {
254
+ // Update pulledAt to reflect this push
255
+ file.metadata.pulledAt = new Date().toISOString();
256
+ file.metadata.version = (file.metadata.version || 0) + 1;
257
+ const updatedContent = buildRequirementsFile(file.metadata, file.markdownContent);
258
+ fs.writeFileSync(file.filePath, updatedContent, 'utf-8');
259
+ console.log(` ✓ Updated: ${fileName}`);
260
+ updated++;
261
+ }
262
+ }
263
+ catch (err) {
264
+ console.error(` ✗ Failed: ${fileName} - ${err.message}`);
265
+ }
266
+ }
267
+ console.log(`\n✓ Push complete!`);
268
+ if (created > 0) {
269
+ console.log(` Created: ${created} document(s)`);
270
+ }
271
+ if (updated > 0) {
272
+ console.log(` Updated: ${updated} document(s)`);
273
+ }
274
+ if (created === 0 && updated === 0) {
275
+ console.log(' No documents were pushed.');
276
+ }
277
+ }
278
+ catch (error) {
279
+ // Re-throw to let the CLI error handler display it cleanly
280
+ throw error;
281
+ }
282
+ }
283
+ /**
284
+ * Extract markdown content from a file, stripping YAML frontmatter.
285
+ * Exported for testing.
286
+ */
287
+ export function extractMarkdownContent(rawContent) {
288
+ // Match YAML frontmatter: starts with ---, ends with ---
289
+ const frontmatterMatch = rawContent.match(/^---\n[\s\S]*?\n---\n*/);
290
+ if (frontmatterMatch) {
291
+ return rawContent.slice(frontmatterMatch[0].length);
292
+ }
293
+ return rawContent;
294
+ }
295
+ /**
296
+ * Prompt user for confirmation
297
+ */
298
+ function confirm(question) {
299
+ const rl = readline.createInterface({
300
+ input: process.stdin,
301
+ output: process.stdout,
302
+ });
303
+ return new Promise((resolve) => {
304
+ rl.question(question, (answer) => {
305
+ rl.close();
306
+ resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
307
+ });
308
+ });
309
+ }
310
+ //# sourceMappingURL=push.js.map
@@ -0,0 +1,6 @@
1
+ interface TestOptions {
2
+ file?: string;
3
+ }
4
+ export declare function testCommand(options: TestOptions): Promise<void>;
5
+ export {};
6
+ //# sourceMappingURL=test.d.ts.map
@@ -0,0 +1,78 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import { parseRequirementsFromFile } from '../schema/index.js';
4
+ export async function testCommand(options) {
5
+ try {
6
+ console.log('Parsing and validating requirements...');
7
+ const requirementsDir = path.join(process.cwd(), '.requirements');
8
+ if (!fs.existsSync(requirementsDir)) {
9
+ console.error('Error: .requirements/ directory not found.');
10
+ console.log('Run "dotrequirements pull" first to sync requirements.');
11
+ process.exit(1);
12
+ }
13
+ // Get files to test
14
+ let files;
15
+ if (options.file) {
16
+ files = [options.file];
17
+ }
18
+ else {
19
+ files = fs
20
+ .readdirSync(requirementsDir)
21
+ .filter((f) => f.endsWith('.requirements.md'))
22
+ .map((f) => path.join(requirementsDir, f));
23
+ }
24
+ if (files.length === 0) {
25
+ console.log('No Markdown requirements files found to test.');
26
+ return;
27
+ }
28
+ let totalRequirements = 0;
29
+ let passCount = 0;
30
+ let failCount = 0;
31
+ for (const filePath of files) {
32
+ const fileName = path.basename(filePath);
33
+ console.log(`\nTesting: ${fileName}`);
34
+ try {
35
+ // Parse and validate using shared schema module
36
+ const { metadata, requirements } = parseRequirementsFromFile(filePath);
37
+ // Count total requirements (root + all descendants)
38
+ let count = 0;
39
+ for (const req of requirements) {
40
+ count += countRequirementsInTree(req);
41
+ }
42
+ totalRequirements += count;
43
+ console.log(` ✓ Valid structure (${count} requirement(s))`);
44
+ console.log(` ✓ Version: ${metadata.version}`);
45
+ console.log(` ✓ Last sync: ${metadata.pulledAt}`);
46
+ passCount++;
47
+ }
48
+ catch (error) {
49
+ console.error(` ✗ Validation failed:`, error);
50
+ failCount++;
51
+ }
52
+ }
53
+ console.log(`\n${'='.repeat(50)}`);
54
+ console.log(`Total files: ${files.length}`);
55
+ console.log(`Passed: ${passCount}`);
56
+ console.log(`Failed: ${failCount}`);
57
+ console.log(`Total requirements: ${totalRequirements}`);
58
+ if (failCount > 0) {
59
+ process.exit(1);
60
+ }
61
+ }
62
+ catch (error) {
63
+ // Re-throw to let the CLI error handler display it cleanly
64
+ throw error;
65
+ }
66
+ }
67
+ /**
68
+ * Recursively count requirements in a tree (1 for root + all descendants)
69
+ */
70
+ function countRequirementsInTree(node) {
71
+ let count = 1; // Count this node
72
+ // Count all children
73
+ for (const child of node.children) {
74
+ count += countRequirementsInTree(child);
75
+ }
76
+ return count;
77
+ }
78
+ //# sourceMappingURL=test.js.map
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Get the Convex deployment URL
3
+ */
4
+ export declare function getConvexUrl(): string;
5
+ //# sourceMappingURL=config.d.ts.map
package/dist/config.js ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Production Convex deployment URL
3
+ * All users connect to the same backend
4
+ */
5
+ const CONVEX_URL = 'https://data.dotrequirements.io';
6
+ /**
7
+ * Get the Convex deployment URL
8
+ */
9
+ export function getConvexUrl() {
10
+ // Show deprecation warning if user has CONVEX_URL in their env
11
+ if (process.env.CONVEX_URL) {
12
+ console.warn('⚠️ CONVEX_URL is no longer needed and will be ignored. You can safely remove it from your .env.local');
13
+ }
14
+ return CONVEX_URL;
15
+ }
16
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Convex API stub for CLI usage
3
+ * This will be replaced with actual generated types when linked to the Convex backend
4
+ */
5
+ export declare const api: {
6
+ documents: {
7
+ queries: {
8
+ exportForCli: any;
9
+ exportProjectForCli: any;
10
+ checkDocumentsExist: any;
11
+ getDocumentsMetadata: any;
12
+ };
13
+ mutations: {
14
+ create: any;
15
+ update: any;
16
+ };
17
+ saveWithRequirements: {
18
+ saveWithRequirements: any;
19
+ };
20
+ };
21
+ requirements: {
22
+ queries: {
23
+ listByProject: any;
24
+ };
25
+ mutations: {
26
+ bulkUpdate: any;
27
+ };
28
+ };
29
+ projects: {
30
+ mutations: {
31
+ create: any;
32
+ };
33
+ };
34
+ teams: {
35
+ queries: {
36
+ listForUserWithUsage: any;
37
+ };
38
+ };
39
+ projectSecrets: {
40
+ queries: {
41
+ getOwnSecret: any;
42
+ listOwnSecretsForTeam: any;
43
+ };
44
+ mutations: {
45
+ ensureOwnSecret: any;
46
+ revokeOwnSecret: any;
47
+ };
48
+ };
49
+ lib: {
50
+ authActions: {
51
+ exchangeAuthorizationCode: any;
52
+ refreshAccessToken: any;
53
+ };
54
+ };
55
+ };
56
+ //# sourceMappingURL=convex.d.ts.map
package/dist/convex.js ADDED
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Convex API stub for CLI usage
3
+ * This will be replaced with actual generated types when linked to the Convex backend
4
+ */
5
+ export const api = {
6
+ documents: {
7
+ queries: {
8
+ exportForCli: 'documents/queries:exportForCli',
9
+ exportProjectForCli: 'documents/queries:exportProjectForCli',
10
+ checkDocumentsExist: 'documents/queries:checkDocumentsExist',
11
+ // SYNC-CLI-EDIT-2: Get document metadata for conflict detection
12
+ getDocumentsMetadata: 'documents/queries:getDocumentsMetadata',
13
+ },
14
+ mutations: {
15
+ create: 'documents/mutations:create',
16
+ update: 'documents/mutations:update',
17
+ },
18
+ // SYNC-ARCH-1: Save document with requirements derivation
19
+ saveWithRequirements: {
20
+ saveWithRequirements: 'documents/saveWithRequirements:saveWithRequirements',
21
+ },
22
+ },
23
+ requirements: {
24
+ queries: {
25
+ listByProject: 'requirements/queries:listByProject',
26
+ },
27
+ mutations: {
28
+ bulkUpdate: 'requirements/mutations:bulkUpdate',
29
+ },
30
+ },
31
+ projects: {
32
+ mutations: {
33
+ create: 'projects/mutations:create',
34
+ },
35
+ },
36
+ teams: {
37
+ queries: {
38
+ listForUserWithUsage: 'teams/queries:listForUserWithUsage',
39
+ },
40
+ },
41
+ projectSecrets: {
42
+ queries: {
43
+ getOwnSecret: 'projectSecrets/queries:getOwnSecret',
44
+ listOwnSecretsForTeam: 'projectSecrets/queries:listOwnSecretsForTeam',
45
+ },
46
+ mutations: {
47
+ ensureOwnSecret: 'projectSecrets/mutations:ensureOwnSecret',
48
+ revokeOwnSecret: 'projectSecrets/mutations:revokeOwnSecret',
49
+ },
50
+ },
51
+ lib: {
52
+ authActions: {
53
+ exchangeAuthorizationCode: 'lib/authActions:exchangeAuthorizationCode',
54
+ refreshAccessToken: 'lib/authActions:refreshAccessToken',
55
+ },
56
+ },
57
+ };
58
+ //# sourceMappingURL=convex.js.map
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Cache management for the test harness.
3
+ *
4
+ * Manages the .requirements/.cache/ directory with:
5
+ * - lookup.json: Pre-resolved requirements for fast path resolution
6
+ * - tracking.jsonl: Append-only tracking of requirement() calls (cross-process safe)
7
+ * - coverage.json: Deduplication cache for cloud reporting
8
+ */
9
+ import { type RequirementNode } from '../schema/index.js';
10
+ /**
11
+ * Tracking entry for a single requirement() call.
12
+ * Stored as one JSON object per line in tracking.jsonl
13
+ */
14
+ export interface TrackingEntry {
15
+ /** Full requirement path (e.g., 'REQ-123.given' or 'REQ-123.0.1') */
16
+ requirementKey: string;
17
+ /** Source file and line (e.g., 'auth.test.ts:42') */
18
+ callerLocation: string;
19
+ /** Timestamp of the call */
20
+ timestamp: number;
21
+ /** Test run ID for grouping */
22
+ testRunId: string;
23
+ }
24
+ /**
25
+ * Structure of the lookup.json cache
26
+ */
27
+ export interface LookupCache {
28
+ /** When the cache was generated */
29
+ generatedAt: string;
30
+ /** Map of requirement ID to flattened requirement data */
31
+ requirements: Record<string, {
32
+ id: string;
33
+ label: string;
34
+ content: string;
35
+ }>;
36
+ }
37
+ /**
38
+ * Structure of the coverage.json cache (for cloud deduplication)
39
+ */
40
+ export interface CoverageCache {
41
+ current: {
42
+ testRunId: string;
43
+ timestamp: number;
44
+ requirementKeys: string[];
45
+ } | null;
46
+ previous: {
47
+ testRunId: string;
48
+ timestamp: number;
49
+ requirementKeys: string[];
50
+ } | null;
51
+ }
52
+ /**
53
+ * Find the nearest .requirements directory by walking up from startDir
54
+ */
55
+ export declare function findRequirementsDir(startDir?: string): string | null;
56
+ /**
57
+ * Find the project root (directory containing .requirements)
58
+ */
59
+ export declare function findProjectRoot(startDir?: string): string | null;
60
+ /**
61
+ * Get the cache directory path, creating it if necessary
62
+ */
63
+ export declare function getCacheDir(requirementsDir: string, create?: boolean): string;
64
+ /**
65
+ * Find the dotrequirements project root by looking for .env.local with credentials.
66
+ * Falls back to finding .requirements directory if no credentials found.
67
+ */
68
+ export declare function findProjectRootWithCredentials(startDir?: string): string;
69
+ /**
70
+ * Find all *.requirements.md files under project root
71
+ */
72
+ export declare function findRequirementsFiles(projectRoot: string): string[];
73
+ /**
74
+ * Write the lookup cache with all pre-parsed requirements
75
+ */
76
+ export declare function writeLookupCache(requirementsDir: string, requirements: RequirementNode[]): void;
77
+ /**
78
+ * Read the lookup cache, returning null if not found or invalid
79
+ */
80
+ export declare function readLookupCache(requirementsDir: string): LookupCache | null;
81
+ /**
82
+ * Generate and store a unique test run ID
83
+ */
84
+ export declare function initTestRunId(requirementsDir: string): string;
85
+ /**
86
+ * Get the current test run ID, or null if not initialized
87
+ */
88
+ export declare function getTestRunId(requirementsDir: string): string | null;
89
+ /**
90
+ * Clean up the test run ID file
91
+ */
92
+ export declare function cleanupTestRunId(requirementsDir: string): void;
93
+ /**
94
+ * Write the project root to cache (for cross-process persistence).
95
+ * This allows test workers to find the lookup cache even after cwd changes.
96
+ */
97
+ export declare function writeProjectRoot(requirementsDir: string, projectRoot: string): void;
98
+ /**
99
+ * Read the project root from cache, returning null if not found.
100
+ */
101
+ export declare function readProjectRoot(requirementsDir: string): string | null;
102
+ /**
103
+ * Try to find the project root from any known .requirements cache directory.
104
+ * Walks up from cwd looking for .requirements/.cache/project-root file.
105
+ */
106
+ export declare function findCachedProjectRoot(startDir?: string): string | null;
107
+ /**
108
+ * Clear tracking data for a new test run
109
+ */
110
+ export declare function clearTrackingFile(requirementsDir: string): void;
111
+ /**
112
+ * Append a tracking entry to the JSONL file (cross-process safe)
113
+ */
114
+ export declare function appendTrackingEntry(requirementsDir: string, entry: TrackingEntry): void;
115
+ /**
116
+ * Read all tracking entries from the JSONL file
117
+ */
118
+ export declare function readTrackingEntries(requirementsDir: string): TrackingEntry[];
119
+ /**
120
+ * Delete the tracking file after finalize
121
+ */
122
+ export declare function deleteTrackingFile(requirementsDir: string): void;
123
+ /**
124
+ * Read the coverage cache for cloud deduplication
125
+ */
126
+ export declare function readCoverageCache(requirementsDir: string): CoverageCache;
127
+ /**
128
+ * Update the coverage cache after reporting
129
+ */
130
+ export declare function updateCoverageCache(requirementsDir: string, testRunId: string, requirementKeys: string[]): void;
131
+ /**
132
+ * Check if a requirement needs reporting based on coverage cache
133
+ */
134
+ export declare function needsReporting(requirementKey: string, cache: CoverageCache): boolean;
135
+ //# sourceMappingURL=cache.d.ts.map