@popoverai/dotrequirements 0.24.0 → 0.24.1

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 CHANGED
@@ -9,7 +9,7 @@ Tests prove *something* works—but nobody is certain it's the right something.
9
9
 
10
10
  **dot•requirements** closes this gap. Write requirements as structured Markdown, reference them directly in tests, and see coverage update automatically. When a requirement changes, the tests that validate it are one click away.
11
11
 
12
- > **Alpha Software** — Under active development. Please report issues to support@popover.ca.
12
+ > **Alpha Software** — Under active development. Please report issues to support@dotrequirements.io.
13
13
 
14
14
  ## Who Is This For?
15
15
 
@@ -253,7 +253,7 @@ dotreq cts skill-install # install the conversational skill wr
253
253
 
254
254
  `cts` shells out to `claude -p` and uses whatever auth mode you've configured for Claude Code. Each pipeline stage is also runnable on its own (`dotreq cts pack`, `plan-loop`, `fan-out`, `compose`, `edit-loop`, `present`) for partial re-runs and debugging.
255
255
 
256
- > **Alpha:** output quality is prompt-sensitive and varies by codebase. See the [Codebase to Spec docs](https://dotrequirements.io/tools/cli/codebase-to-spec) for prerequisites, options, exit codes, and known rough edges. Feedback to support@popover.ca welcome.
256
+ > **Alpha:** output quality is prompt-sensitive and varies by codebase. See the [Codebase to Spec docs](https://docs.dotrequirements.io/tools/cli/codebase-to-spec) for prerequisites, options, exit codes, and known rough edges. Feedback to support@dotrequirements.io welcome.
257
257
 
258
258
  > **Heads up:** as of June 15, 2026, `claude -p` bills against your Claude subscription's API credit instead of the subscription seat (per Anthropic's May 13, 2026 announcement). `cts` runs will draw from that credit.
259
259
 
@@ -725,7 +725,7 @@ This file is automatically added to `.gitignore` during initialization.
725
725
 
726
726
  - [Documentation](https://docs.dotrequirements.io)
727
727
  - [Getting Started Guide](https://docs.dotrequirements.io/getting-started)
728
- - [Support](mailto:support@popover.ca)
728
+ - [Support](mailto:support@dotrequirements.io)
729
729
 
730
730
  ---
731
731
 
package/dist/cli.js CHANGED
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@popoverai/dotrequirements",
3
- "version": "0.24.0",
3
+ "version": "0.24.1",
4
4
  "description": "Requirements tracking CLI, test harness, and MCP server",
5
5
  "type": "module",
6
6
  "bin": {
@@ -42,7 +42,7 @@
42
42
  "license": "MIT",
43
43
  "homepage": "https://dotrequirements.io",
44
44
  "bugs": {
45
- "email": "support@popover.ca"
45
+ "email": "support@dotrequirements.io"
46
46
  },
47
47
  "engines": {
48
48
  "node": ">=18"
@@ -1,12 +0,0 @@
1
- /**
2
- * System prompt for the applying planner (apply mode).
3
- *
4
- * Receives the prior outline + an explicit list of mechanical revisions and
5
- * applies them verbatim. Used when the reviewer's verdict was
6
- * `approved-with-revisions`.
7
- *
8
- * Requirements covered:
9
- * - CTS-PLAN-4: Approved-with-revisions outline triggers one mechanical revision
10
- */
11
- export declare const PLANNER_APPLY_PROMPT = "You are applying mechanical revisions to a planning outline. A reviewer has approved the outline subject to a specific list of small revisions. Your job is to apply each revision verbatim and emit the revised outline.\n\nYou are NOT revising the outline using your own judgment. You are NOT adding new areas, dropping existing areas, or restructuring. You ONLY apply the listed revisions.\n\nYou will output a JSON object matching the schema you have been given. No prose preamble, no explanation, no markdown fences \u2014 JSON only.\n\nYou will receive:\n1. The previous outline as JSON\n2. An explicit list of revisions, each phrased as a directive\n\n## Process\n\nFor each revision in the list:\n- Apply the directive exactly as stated.\n- If the directive is ambiguous or would require new judgment, leave that area unchanged and proceed.\n\nPreserve everything else from the prior outline byte-for-byte (modulo the listed changes).\n\n## Output\n\nOutput ONLY the JSON object matching the supplied schema. No preamble. Begin with `{`.";
12
- //# sourceMappingURL=planner-apply.d.ts.map
@@ -1,32 +0,0 @@
1
- /**
2
- * System prompt for the applying planner (apply mode).
3
- *
4
- * Receives the prior outline + an explicit list of mechanical revisions and
5
- * applies them verbatim. Used when the reviewer's verdict was
6
- * `approved-with-revisions`.
7
- *
8
- * Requirements covered:
9
- * - CTS-PLAN-4: Approved-with-revisions outline triggers one mechanical revision
10
- */
11
- export const PLANNER_APPLY_PROMPT = `You are applying mechanical revisions to a planning outline. A reviewer has approved the outline subject to a specific list of small revisions. Your job is to apply each revision verbatim and emit the revised outline.
12
-
13
- You are NOT revising the outline using your own judgment. You are NOT adding new areas, dropping existing areas, or restructuring. You ONLY apply the listed revisions.
14
-
15
- You will output a JSON object matching the schema you have been given. No prose preamble, no explanation, no markdown fences — JSON only.
16
-
17
- You will receive:
18
- 1. The previous outline as JSON
19
- 2. An explicit list of revisions, each phrased as a directive
20
-
21
- ## Process
22
-
23
- For each revision in the list:
24
- - Apply the directive exactly as stated.
25
- - If the directive is ambiguous or would require new judgment, leave that area unchanged and proceed.
26
-
27
- Preserve everything else from the prior outline byte-for-byte (modulo the listed changes).
28
-
29
- ## Output
30
-
31
- Output ONLY the JSON object matching the supplied schema. No preamble. Begin with \`{\`.`;
32
- //# sourceMappingURL=planner-apply.js.map
@@ -1,6 +0,0 @@
1
- interface BrowserTestOptions {
2
- json?: boolean;
3
- }
4
- export declare function browserTestCommand(requirementKey: string, url: string | undefined, options: BrowserTestOptions): Promise<void>;
5
- export {};
6
- //# sourceMappingURL=browsertest.d.ts.map
@@ -1,212 +0,0 @@
1
- import { execFile } from "node:child_process";
2
- import { promisify } from "node:util";
3
- import { formatRequirement, getRequirementTree, loadAllRequirements, } from "../mcp/requirements.js";
4
- import { getProjectInfo, readProjectSettings, } from "../utils/project-settings.js";
5
- const execFileAsync = promisify(execFile);
6
- function extractResults(parsed) {
7
- if (Array.isArray(parsed))
8
- return parsed;
9
- if (parsed && typeof parsed === "object") {
10
- const obj = parsed;
11
- // { results: [...] } format
12
- if (Array.isArray(obj.results))
13
- return obj.results;
14
- // { structured_output: { results: [...] } } format
15
- if (obj.structured_output && typeof obj.structured_output === "object") {
16
- const so = obj.structured_output;
17
- if (Array.isArray(so.results))
18
- return so.results;
19
- }
20
- }
21
- throw new Error("Unexpected browser-automation output format");
22
- }
23
- export async function browserTestCommand(requirementKey, url, options) {
24
- // 1. Find project and load settings
25
- const projectInfo = getProjectInfo();
26
- if (!projectInfo) {
27
- console.error("Error: No dotrequirements project found.");
28
- console.log('Run "dotrequirements init" to create one.');
29
- process.exit(1);
30
- }
31
- let settings;
32
- try {
33
- settings = readProjectSettings(projectInfo.rootPath);
34
- }
35
- catch (err) {
36
- // Settings file exists but is invalid
37
- console.error(`Error: ${err.message}`);
38
- process.exit(1);
39
- }
40
- // 2. Determine URL
41
- const targetURL = url || settings?.defaultURL;
42
- if (!targetURL) {
43
- console.error("Error: No URL provided and no defaultURL in project-settings.json.");
44
- console.log('Either provide a URL argument or add "defaultURL" to .requirements/project-settings.json');
45
- process.exit(1);
46
- }
47
- // 3. Resolve model config
48
- const modelName = settings?.browserTest?.modelName;
49
- const modelApiKey = settings?.browserTest?.modelApiKey;
50
- const geminiApiKey = settings?.browserTest?.geminiApiKey;
51
- if (!modelName && !modelApiKey && !geminiApiKey) {
52
- console.error("Error: no browser-test model configured in project-settings.json.");
53
- console.log("Add a block like:\n" +
54
- ' "browserTest": {\n' +
55
- ' "modelName": "gateway/anthropic/claude-haiku-4-5",\n' +
56
- ' "modelApiKey": "..."\n' +
57
- " }\n" +
58
- "to .requirements/project-settings.json. See the docs for supported models.");
59
- process.exit(1);
60
- }
61
- if ((modelName && !modelApiKey) || (modelApiKey && !modelName)) {
62
- console.error("Error: browserTest.modelName and browserTest.modelApiKey must be set together.");
63
- process.exit(1);
64
- }
65
- // 4. Load requirements
66
- const { flattened } = await loadAllRequirements(projectInfo.rootPath);
67
- if (flattened.length === 0) {
68
- console.error("Error: No requirements found in .requirements/");
69
- process.exit(1);
70
- }
71
- // 5. Find the requirement tree
72
- const requirementTree = getRequirementTree(flattened, requirementKey);
73
- if (requirementTree.length === 0) {
74
- console.error(`Error: Requirement "${requirementKey}" not found.`);
75
- process.exit(1);
76
- }
77
- // 6. Format each requirement as an assertion
78
- const assertions = requirementTree.map((req) => formatRequirement(req));
79
- // 7. Build environment with injected secrets
80
- const env = { ...process.env };
81
- // env var keeps the key out of process listings (not a CLI flag)
82
- if (modelApiKey) {
83
- env.MODEL_API_KEY = modelApiKey;
84
- }
85
- else if (geminiApiKey) {
86
- env.GEMINI_API_KEY = geminiApiKey;
87
- }
88
- if (settings?.browserTest?.vercelBypassSecret) {
89
- env.VERCEL_AUTOMATION_BYPASS_SECRET =
90
- settings.browserTest.vercelBypassSecret;
91
- }
92
- if (settings?.browserTest?.browserbaseApiKey) {
93
- env.BROWSERBASE_API_KEY = settings.browserTest.browserbaseApiKey;
94
- }
95
- if (settings?.browserTest?.browserbaseProjectId) {
96
- env.BROWSERBASE_PROJECT_ID = settings.browserTest.browserbaseProjectId;
97
- }
98
- // 8. Run browser-automation
99
- if (!options.json) {
100
- console.log(`Testing ${requirementKey} against ${targetURL}...\n`);
101
- }
102
- try {
103
- const modelFlags = modelName ? ["--modelName", modelName] : [];
104
- const { stdout } = await execFileAsync("npx", [
105
- "@popoverai/browser-automation",
106
- "test",
107
- ...modelFlags,
108
- targetURL,
109
- ...assertions,
110
- ], { env, maxBuffer: 10 * 1024 * 1024 });
111
- // 9. Parse results
112
- let results;
113
- try {
114
- const parsed = JSON.parse(stdout.trim());
115
- results = extractResults(parsed);
116
- }
117
- catch (parseErr) {
118
- console.error("Error: Could not parse browser-automation output");
119
- console.error(parseErr.message);
120
- console.error(stdout);
121
- process.exit(1);
122
- }
123
- // 10. Display results
124
- if (options.json) {
125
- outputJson(requirementTree, results);
126
- }
127
- else {
128
- displayResults(requirementTree, results);
129
- }
130
- // 11. Exit with appropriate code
131
- const failedCount = results.filter((r) => r.status !== "passed").length;
132
- if (failedCount > 0) {
133
- process.exit(1);
134
- }
135
- }
136
- catch (err) {
137
- const error = err;
138
- // browser-automation returns exit code 1 on failure, but still outputs JSON
139
- if (error.stdout) {
140
- try {
141
- const parsed = JSON.parse(error.stdout.trim());
142
- const results = extractResults(parsed);
143
- if (options.json) {
144
- outputJson(requirementTree, results);
145
- }
146
- else {
147
- displayResults(requirementTree, results);
148
- }
149
- process.exit(1);
150
- }
151
- catch {
152
- // Not JSON, show raw output
153
- console.error("browser-automation error:");
154
- if (error.stdout)
155
- console.error(error.stdout);
156
- if (error.stderr)
157
- console.error(error.stderr);
158
- process.exit(1);
159
- }
160
- }
161
- else {
162
- console.error(`Error running browser-automation: ${error.message}`);
163
- if (error.stderr)
164
- console.error(error.stderr);
165
- process.exit(1);
166
- }
167
- }
168
- }
169
- function displayResults(requirements, results) {
170
- let passedCount = 0;
171
- let failedCount = 0;
172
- for (let i = 0; i < requirements.length; i++) {
173
- const req = requirements[i];
174
- const result = results[i];
175
- if (!result) {
176
- console.log(` ? ${req.id}: No result`);
177
- failedCount++;
178
- continue;
179
- }
180
- const indent = " ".repeat(req.path.length);
181
- const label = req.label ? ` (${req.label})` : "";
182
- const icon = result.status === "passed" ? "\u2713" : "\u2717";
183
- console.log(`${indent}${icon} ${req.id}${label}: ${req.content}`);
184
- if (result.status !== "passed" && result.notes) {
185
- console.log(`${indent} \u2192 ${result.notes}`);
186
- }
187
- if (result.status === "passed") {
188
- passedCount++;
189
- }
190
- else {
191
- failedCount++;
192
- }
193
- }
194
- console.log();
195
- if (failedCount === 0) {
196
- console.log(`${passedCount}/${requirements.length} passed`);
197
- }
198
- else {
199
- console.log(`${passedCount}/${requirements.length} passed, ${failedCount} failed`);
200
- }
201
- }
202
- function outputJson(requirements, results) {
203
- const combined = requirements.map((req, i) => ({
204
- id: req.id,
205
- label: req.label || null,
206
- content: req.content,
207
- status: results[i]?.status || "blocked",
208
- notes: results[i]?.notes || null,
209
- }));
210
- console.log(JSON.stringify(combined, null, 2));
211
- }
212
- //# sourceMappingURL=browsertest.js.map
@@ -1,12 +0,0 @@
1
- /**
2
- * Login command - authenticate with WorkOS and manage project credentials.
3
- *
4
- * Implements:
5
- * - LOGIN-1: CLI OAuth implementation
6
- * - LOGIN-2: Upgrade from placeholder to real credentials
7
- * - LOGIN-3: Refresh existing project credentials
8
- * - LOGIN-4: Already logged in detection
9
- * - LOGIN-6: Handle missing .env.local
10
- */
11
- export declare function loginCommand(): Promise<void>;
12
- //# sourceMappingURL=login.d.ts.map
@@ -1,117 +0,0 @@
1
- /**
2
- * Login command - authenticate with WorkOS and manage project credentials.
3
- *
4
- * Implements:
5
- * - LOGIN-1: CLI OAuth implementation
6
- * - LOGIN-2: Upgrade from placeholder to real credentials
7
- * - LOGIN-3: Refresh existing project credentials
8
- * - LOGIN-4: Already logged in detection
9
- * - LOGIN-6: Handle missing .env.local
10
- */
11
- import { executeOAuthFlow } from '../utils/oauth-flow.js';
12
- import { getConvexUrl } from '../config.js';
13
- import { hasValidTokens } from '../utils/token-storage.js';
14
- import { isLocalOnlyProject } from '../utils/local-project.js';
15
- import * as fs from 'fs';
16
- import { ConvexHttpClient } from 'convex/browser';
17
- import { findUp } from 'find-up';
18
- import { brand } from '../utils/brand.js';
19
- export async function loginCommand() {
20
- console.log(`Logging in to ${brand}...\n`);
21
- // LOGIN-4: Check if already logged in
22
- if (await hasValidTokens()) {
23
- console.log('✓ Already logged in');
24
- console.log('Run "dotrequirements logout" to log in as a different user.');
25
- return;
26
- }
27
- // LOGIN-1: Execute OAuth flow
28
- const result = await executeOAuthFlow();
29
- console.log('\n✓ Logged in successfully!');
30
- // LOGIN-6: Check if there's a .env.local in the current directory
31
- const envPath = await findUp('.env.local');
32
- if (envPath) {
33
- await handleExistingProject(envPath, result.accessToken);
34
- }
35
- else {
36
- // LOGIN-6.1: No project in current directory
37
- console.log('\nNo project found in this directory.');
38
- console.log('Run "dotrequirements init" to create a new project in the current directory.');
39
- }
40
- }
41
- /**
42
- * Handle existing project based on credential type (placeholder vs real)
43
- */
44
- async function handleExistingProject(envPath, accessToken) {
45
- try {
46
- // Read .env.local to get project ID and secret
47
- const envContent = fs.readFileSync(envPath, 'utf-8');
48
- const projectIdMatch = envContent.match(/DOTREQUIREMENTS_PROJECT_ID=(.+)/);
49
- const secretMatch = envContent.match(/DOTREQUIREMENTS_PROJECT_SECRET=(.+)/);
50
- if (!projectIdMatch || !secretMatch) {
51
- console.log('\n.env.local found but missing project credentials.');
52
- return;
53
- }
54
- const projectId = projectIdMatch[1].trim();
55
- const projectSecret = secretMatch[1].trim();
56
- const convexUrl = getConvexUrl();
57
- const client = new ConvexHttpClient(convexUrl);
58
- client.setAuth(accessToken);
59
- // LOGIN-2: Check if placeholder credentials
60
- if (isLocalOnlyProject(projectId)) {
61
- await upgradeFromPlaceholder(envPath, envContent, client);
62
- }
63
- else {
64
- // LOGIN-3: Refresh existing project credentials
65
- await refreshCredentials(envPath, envContent, projectId, client);
66
- }
67
- }
68
- catch (error) {
69
- console.error('\nFailed to update project credentials:');
70
- console.error(error instanceof Error ? error.message : String(error));
71
- console.log('\nYour authentication was successful.');
72
- console.log('You can continue using the project with its existing credentials.');
73
- }
74
- }
75
- /**
76
- * LOGIN-2: Upgrade from placeholder credentials to real project
77
- */
78
- async function upgradeFromPlaceholder(envPath, envContent, client) {
79
- console.log('\nUpgrading from local-only mode to cloud access...');
80
- // LOGIN-2.0, LOGIN-2.1: Create team and project
81
- const result = await client.mutation('lib/authMutations:createProjectForAuthenticatedUser', {});
82
- if (!result.success) {
83
- throw new Error(result.error || 'Unknown error');
84
- }
85
- console.log('✓ Created new project in your team');
86
- // LOGIN-2.3: Update .env.local with real credentials
87
- const updatedEnv = envContent
88
- .replace(/DOTREQUIREMENTS_PROJECT_ID=.+/, `DOTREQUIREMENTS_PROJECT_ID=${result.projectId}`)
89
- .replace(/DOTREQUIREMENTS_PROJECT_SECRET=.+/, `DOTREQUIREMENTS_PROJECT_SECRET=${result.projectSecret}`);
90
- fs.writeFileSync(envPath, updatedEnv);
91
- console.log('✓ Updated .env.local with cloud credentials');
92
- // LOGIN-2.4: Display next steps
93
- console.log('\nYour project is now connected to the cloud!');
94
- console.log('Run "dotrequirements push" to sync your requirements.');
95
- }
96
- /**
97
- * LOGIN-3: Refresh existing project credentials
98
- */
99
- async function refreshCredentials(envPath, envContent, projectId, client) {
100
- console.log('\nRefreshing your project credentials...');
101
- // LOGIN-3.0: Verify access and generate new secret
102
- const result = await client.mutation('lib/authMutations:refreshProjectSecret', {
103
- projectId: projectId, // Cast to Id<"projects">
104
- });
105
- if (!result.success) {
106
- // LOGIN-3.4: User doesn't have access
107
- throw new Error(result.error || 'Unknown error');
108
- }
109
- // LOGIN-3.2: Update .env.local with new secret
110
- const updatedEnv = envContent.replace(/DOTREQUIREMENTS_PROJECT_SECRET=.+/, `DOTREQUIREMENTS_PROJECT_SECRET=${result.projectSecret}`);
111
- fs.writeFileSync(envPath, updatedEnv);
112
- // LOGIN-3.3: Confirm access extended
113
- const expiryDate = new Date(result.expiresAt).toLocaleDateString();
114
- console.log(`✓ Access extended until ${expiryDate}`);
115
- console.log('✓ Updated .env.local with new credentials');
116
- }
117
- //# sourceMappingURL=login.js.map
@@ -1,5 +0,0 @@
1
- /**
2
- * Logout command - clear stored authentication tokens.
3
- */
4
- export declare function logoutCommand(): Promise<void>;
5
- //# sourceMappingURL=logout.d.ts.map
@@ -1,17 +0,0 @@
1
- /**
2
- * Logout command - clear stored authentication tokens.
3
- */
4
- import { deleteTokens, hasValidTokens } from '../utils/token-storage.js';
5
- export async function logoutCommand() {
6
- const wasLoggedIn = await hasValidTokens();
7
- await deleteTokens();
8
- if (wasLoggedIn) {
9
- console.log('Logged out successfully.');
10
- }
11
- else {
12
- console.log('No active session found.');
13
- }
14
- console.log('\nYour project credentials in .env.local are unchanged.');
15
- console.log('Run "dotrequirements login" to authenticate again.');
16
- }
17
- //# sourceMappingURL=logout.js.map
@@ -1,5 +0,0 @@
1
- /**
2
- * MCP setup command - configures the MCP server for AI assistants
3
- */
4
- export declare function mcpSetupCommand(): Promise<void>;
5
- //# sourceMappingURL=mcp-setup.d.ts.map