@popoverai/dotrequirements 0.13.0 → 0.15.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 (57) hide show
  1. package/README.md +92 -48
  2. package/dist/cli.js +1 -10
  3. package/dist/commands/init.js +166 -226
  4. package/dist/commands/link.d.ts +9 -10
  5. package/dist/commands/link.js +81 -106
  6. package/dist/commands/mcp-setup.js +77 -94
  7. package/dist/commands/pull.d.ts +1 -0
  8. package/dist/commands/pull.js +70 -53
  9. package/dist/commands/push.js +4 -13
  10. package/dist/config.js +0 -4
  11. package/dist/harness/cache.d.ts +0 -5
  12. package/dist/harness/cache.js +0 -48
  13. package/dist/harness/convexReporting.js +7 -14
  14. package/dist/harness/finalize.js +7 -12
  15. package/dist/harness/prepare.js +7 -9
  16. package/dist/mcp/convexClient.d.ts +9 -1
  17. package/dist/mcp/convexClient.js +15 -35
  18. package/dist/mcp/index.js +112 -152
  19. package/dist/mcp/requirements.d.ts +10 -0
  20. package/dist/mcp/requirements.js +15 -1
  21. package/dist/templates/context-file-section.md +59 -0
  22. package/dist/utils/context-file.d.ts +38 -0
  23. package/dist/utils/context-file.js +94 -0
  24. package/dist/utils/env.d.ts +0 -13
  25. package/dist/utils/env.js +0 -19
  26. package/dist/utils/gitignore.d.ts +2 -2
  27. package/dist/utils/gitignore.js +4 -4
  28. package/dist/utils/oauth-flow.d.ts +0 -1
  29. package/dist/utils/oauth-flow.js +0 -9
  30. package/dist/utils/project-discovery.d.ts +3 -5
  31. package/dist/utils/project-discovery.js +18 -42
  32. package/dist/utils/project-selector.d.ts +17 -3
  33. package/dist/utils/project-selector.js +35 -3
  34. package/dist/utils/project-settings.d.ts +56 -0
  35. package/dist/utils/project-settings.js +126 -0
  36. package/dist/utils/templates.d.ts +0 -24
  37. package/dist/utils/templates.js +0 -39
  38. package/package.json +1 -1
  39. package/dist/harness/localReporting.d.ts +0 -6
  40. package/dist/harness/localReporting.js +0 -49
  41. package/dist/templates/antigravity-gemini.md +0 -3
  42. package/dist/templates/antigravity-overview-rule.md +0 -3
  43. package/dist/templates/antigravity-test-rule.md +0 -3
  44. package/dist/templates/behavioral-core.md +0 -25
  45. package/dist/templates/claude-code-overview-skill.md +0 -6
  46. package/dist/templates/claude-code-skill.md +0 -6
  47. package/dist/templates/claude-code-test-skill.md +0 -6
  48. package/dist/templates/codex-agents.md +0 -3
  49. package/dist/templates/codex-overview-agents.md +0 -3
  50. package/dist/templates/codex-test-agents.md +0 -3
  51. package/dist/templates/cursor-overview-rule.mdc +0 -5
  52. package/dist/templates/cursor-rule.mdc +0 -5
  53. package/dist/templates/cursor-test-rule.mdc +0 -5
  54. package/dist/templates/overview-core.md +0 -27
  55. package/dist/templates/test-writing-core.md +0 -72
  56. package/dist/utils/detect-existing-project.d.ts +0 -5
  57. package/dist/utils/detect-existing-project.js +0 -34
@@ -5,24 +5,15 @@ import { getConvexUrl } from '../config.js';
5
5
  import { parseRequirementsFromFile, getAllRequirements, parseRequirementKey, buildRequirementsFile } from '../schema/index.js';
6
6
  import { api } from '../convex.js';
7
7
  import * as readline from 'readline';
8
- import { isLocalOnlyProject, CLOUD_FEATURES_REQUIRE_AUTH_MESSAGE } from '../utils/local-project.js';
9
8
  import { brand } from '../utils/brand.js';
10
9
  import { findRequirementsFiles } from '../mcp/requirements.js';
10
+ import { getProjectCredentials } from '../utils/project-settings.js';
11
11
  export async function pushCommand(file, options) {
12
12
  try {
13
13
  console.log(`Preparing to push requirements to ${brand} cloud...\n`);
14
- // Verify project credentials are configured
15
- const projectId = process.env.DOTREQUIREMENTS_PROJECT_ID;
16
- const projectSecret = process.env.DOTREQUIREMENTS_PROJECT_SECRET;
17
- if (!projectId || !projectSecret) {
18
- throw new Error('Project credentials not found. Either:\n' +
19
- ' 1. Run "dotreq init" to initialize the project, or\n' +
20
- ' 2. Set DOTREQUIREMENTS_PROJECT_ID and DOTREQUIREMENTS_PROJECT_SECRET in .env.local');
21
- }
22
- // AUTHZ-2.0: Check if project is local-only
23
- if (isLocalOnlyProject(projectId)) {
24
- throw new Error(CLOUD_FEATURES_REQUIRE_AUTH_MESSAGE);
25
- }
14
+ // Get project credentials from .requirements/project-settings.json
15
+ // This throws helpful errors if project not found or not connected to cloud
16
+ const { projectId, projectSecret } = getProjectCredentials();
26
17
  // Determine which files to push
27
18
  let filesToPush;
28
19
  if (file) {
package/dist/config.js CHANGED
@@ -7,10 +7,6 @@ const CONVEX_URL = 'https://data.dotrequirements.io';
7
7
  * Get the Convex deployment URL
8
8
  */
9
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
10
  return CONVEX_URL;
15
11
  }
16
12
  //# sourceMappingURL=config.js.map
@@ -61,11 +61,6 @@ export declare function findProjectRoot(startDir?: string): string | null;
61
61
  * Get the cache directory path, creating it if necessary
62
62
  */
63
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
64
  /**
70
65
  * Find all *.requirements.md files under project root
71
66
  */
@@ -9,8 +9,6 @@
9
9
  import * as fs from 'fs';
10
10
  import * as path from 'path';
11
11
  import { globSync } from 'glob';
12
- import { findUpSync } from 'find-up';
13
- import { brandPlain } from '../utils/brand.js';
14
12
  // Cache directory structure
15
13
  const CACHE_DIR = '.cache';
16
14
  const LOOKUP_FILE = 'lookup.json';
@@ -54,52 +52,6 @@ export function getCacheDir(requirementsDir, create = false) {
54
52
  }
55
53
  return cacheDir;
56
54
  }
57
- /**
58
- * Check if an .env.local file has dotrequirements credentials.
59
- */
60
- function hasDotrequirementsCredentials(envPath) {
61
- const envContent = fs.readFileSync(envPath, 'utf-8');
62
- const hasProjectId = envContent.includes('DOTREQUIREMENTS_PROJECT_ID=');
63
- const hasProjectSecret = envContent.includes('DOTREQUIREMENTS_PROJECT_SECRET=');
64
- return hasProjectId && hasProjectSecret;
65
- }
66
- /**
67
- * Find the dotrequirements project root by looking for .env.local with credentials.
68
- * Falls back to finding .requirements directory if no credentials found.
69
- */
70
- export function findProjectRootWithCredentials(startDir = process.cwd()) {
71
- let searchDir = startDir;
72
- while (true) {
73
- const envPath = findUpSync('.env.local', { cwd: searchDir });
74
- if (!envPath) {
75
- // No .env.local found - try to find project root by .requirements directory
76
- const projectRoot = findProjectRoot(startDir);
77
- if (projectRoot) {
78
- return projectRoot;
79
- }
80
- throw new Error(`Could not find ${brandPlain} project root. ` +
81
- `Expected to find .env.local with credentials or a .requirements directory.`);
82
- }
83
- // Check if this .env.local has dotrequirements credentials
84
- if (hasDotrequirementsCredentials(envPath)) {
85
- return path.dirname(envPath);
86
- }
87
- // No credentials - continue searching from parent directory
88
- const envDir = path.dirname(envPath);
89
- const parentDir = path.dirname(envDir);
90
- // If we've reached the root, stop
91
- if (parentDir === envDir) {
92
- // Try .requirements as fallback
93
- const projectRoot = findProjectRoot(startDir);
94
- if (projectRoot) {
95
- return projectRoot;
96
- }
97
- throw new Error(`Could not find ${brandPlain} project root. ` +
98
- `Found .env.local files but none contained credentials.`);
99
- }
100
- searchDir = parentDir;
101
- }
102
- }
103
55
  /**
104
56
  * Find all *.requirements.md files under project root
105
57
  */
@@ -1,8 +1,7 @@
1
1
  import { execSync } from "child_process";
2
2
  import { getTrackedRequirements } from "./tracking.js";
3
3
  import { saveCache, getRequirementsToReport } from "./coverageCache.js";
4
- import { isLocalOnlyProject } from "../utils/local-project.js";
5
- import { loadEnvFile, getProjectCredentials } from "../utils/env.js";
4
+ import { getProjectInfo } from "../utils/project-settings.js";
6
5
  /**
7
6
  * Get the current git branch, or null if not in a git repo
8
7
  */
@@ -51,20 +50,14 @@ function parseCallerLocation(location) {
51
50
  */
52
51
  export async function reportCoverageToConvex(cwd = process.cwd(), trackedReqs) {
53
52
  try {
54
- // Load credentials from .env.local (standard credential discovery)
55
- loadEnvFile(cwd);
56
- const credentials = getProjectCredentials();
57
- // Skip if credentials are missing
58
- if (!credentials) {
59
- console.log("\nℹ️ Skipping cloud coverage reporting (DOTREQUIREMENTS_PROJECT_ID or DOTREQUIREMENTS_PROJECT_SECRET not configured)");
60
- return;
61
- }
62
- const { projectId, projectSecret } = credentials;
63
- // AUTHZ-1.2: Skip cloud reporting for local-only projects (silent)
64
- if (isLocalOnlyProject(projectId)) {
65
- // Silently skip - local-only projects track coverage in-memory only
53
+ // Get project info from .requirements/project-settings.json
54
+ const projectInfo = getProjectInfo(cwd);
55
+ // Skip if no project found or not connected to cloud
56
+ if (!projectInfo || projectInfo.status !== 'cloud-connected' || !projectInfo.credentials) {
57
+ console.log("\nℹ️ Skipping cloud coverage reporting (project not connected to cloud)");
66
58
  return;
67
59
  }
60
+ const { projectId, projectSecret } = projectInfo.credentials;
68
61
  // Use provided trackedReqs or fall back to in-memory tracking
69
62
  const reqs = trackedReqs ?? getTrackedRequirements();
70
63
  const requirementKeys = Array.from(reqs.keys());
@@ -11,8 +11,7 @@
11
11
  */
12
12
  import { execSync } from 'child_process';
13
13
  import { findRequirementsDir, findProjectRoot, getTestRunId, cleanupTestRunId, readTrackingEntries, deleteTrackingFile, readLookupCache, readCoverageCache, updateCoverageCache, needsReporting, } from './cache.js';
14
- import { loadEnvFile, getProjectCredentials } from '../utils/env.js';
15
- import { isLocalOnlyProject } from '../utils/local-project.js';
14
+ import { getProjectInfo } from '../utils/project-settings.js';
16
15
  /**
17
16
  * Aggregate tracking entries by requirement key
18
17
  */
@@ -115,20 +114,16 @@ const CONVEX_URL = 'https://data.dotrequirements.io';
115
114
  */
116
115
  async function reportToConvex(projectRoot, testRunId, aggregated, showCloudStatus) {
117
116
  try {
118
- // Load credentials
119
- loadEnvFile(projectRoot);
120
- const credentials = getProjectCredentials();
121
- if (!credentials) {
117
+ // Get project info from .requirements/project-settings.json
118
+ const projectInfo = getProjectInfo(projectRoot);
119
+ // Skip if no project found or not connected to cloud
120
+ if (!projectInfo || projectInfo.status !== 'cloud-connected' || !projectInfo.credentials) {
122
121
  if (showCloudStatus) {
123
- console.log('\nℹ️ Skipping cloud coverage reporting (DOTREQUIREMENTS_PROJECT_ID or DOTREQUIREMENTS_PROJECT_SECRET not configured)');
122
+ console.log('\nℹ️ Skipping cloud coverage reporting (project not connected to cloud)');
124
123
  }
125
124
  return { sent: false, count: 0 };
126
125
  }
127
- const { projectId, projectSecret } = credentials;
128
- // Skip for local-only projects
129
- if (isLocalOnlyProject(projectId)) {
130
- return { sent: false, count: 0 };
131
- }
126
+ const { projectId, projectSecret } = projectInfo.credentials;
132
127
  const requirementsDir = findRequirementsDir(projectRoot);
133
128
  // Get all tracked requirement keys
134
129
  const requirementKeys = Array.from(aggregated.keys());
@@ -8,8 +8,9 @@
8
8
  *
9
9
  * Implements: HARNESS-PREPARE-1, HARNESS-PREPARE-2, HARNESS-PREPARE-3
10
10
  */
11
- import { findProjectRootWithCredentials, findRequirementsDir, findRequirementsFiles, writeLookupCache, initTestRunId, clearTrackingFile, writeProjectRoot, } from './cache.js';
11
+ import { findRequirementsDir, findRequirementsFiles, writeLookupCache, initTestRunId, clearTrackingFile, writeProjectRoot, } from './cache.js';
12
12
  import { parseRequirementsFromFile, } from '../schema/index.js';
13
+ import { findProjectRoot } from '../utils/project-settings.js';
13
14
  /**
14
15
  * Prepare the test harness for a test run.
15
16
  *
@@ -22,14 +23,11 @@ import { parseRequirementsFromFile, } from '../schema/index.js';
22
23
  */
23
24
  export function prepare(options = {}) {
24
25
  const { cwd = process.cwd(), logWarnings = true } = options;
25
- // Find the project root - prefer root with credentials (.env.local) for monorepo support
26
- let projectRoot;
27
- try {
28
- projectRoot = findProjectRootWithCredentials(cwd);
29
- }
30
- catch {
31
- throw new Error('Could not find .requirements directory or .env.local with credentials. ' +
32
- 'Please create a .requirements directory in your project root.');
26
+ // Find the project root by looking for .requirements directory
27
+ const projectRoot = findProjectRoot(cwd);
28
+ if (!projectRoot) {
29
+ throw new Error('Could not find .requirements directory. ' +
30
+ 'Please run "dotrequirements init" to initialize your project.');
33
31
  }
34
32
  // Set environment variable for cross-process persistence (globalSetup → test workers)
35
33
  process.env.DOTREQUIREMENTS_PROJECT_ROOT = projectRoot;
@@ -1,11 +1,19 @@
1
1
  /**
2
- * Load Convex configuration from .env file or environment variables
2
+ * Production Convex deployment URL
3
+ */
4
+ export declare const CONVEX_URL = "https://data.dotrequirements.io";
5
+ /**
6
+ * Convex configuration for cloud operations
3
7
  */
4
8
  interface ConvexConfig {
5
9
  convexUrl: string;
6
10
  projectId: string;
7
11
  projectSecret: string;
8
12
  }
13
+ /**
14
+ * Load Convex configuration from project settings.
15
+ * Returns null if credentials are not configured.
16
+ */
9
17
  export declare function loadConvexConfig(cwd?: string): ConvexConfig | null;
10
18
  /**
11
19
  * Query Convex for requirement coverage
@@ -1,45 +1,25 @@
1
- import { readFileSync, existsSync } from 'fs';
2
- import { join } from 'path';
1
+ import { getProjectCredentials } from '../utils/project-settings.js';
3
2
  /**
4
3
  * Production Convex deployment URL
5
4
  */
6
- const CONVEX_URL = 'https://data.dotrequirements.io';
5
+ export const CONVEX_URL = 'https://data.dotrequirements.io';
6
+ /**
7
+ * Load Convex configuration from project settings.
8
+ * Returns null if credentials are not configured.
9
+ */
7
10
  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;
11
+ try {
12
+ const credentials = getProjectCredentials(cwd);
13
+ return {
14
+ convexUrl: CONVEX_URL,
15
+ projectId: credentials.projectId,
16
+ projectSecret: credentials.projectSecret,
17
+ };
38
18
  }
39
- if (!projectId || !projectSecret) {
19
+ catch {
20
+ // Credentials not found or project not connected to cloud
40
21
  return null;
41
22
  }
42
- return { convexUrl: CONVEX_URL, projectId, projectSecret };
43
23
  }
44
24
  /**
45
25
  * Query Convex for requirement coverage