@popoverai/dotrequirements 0.26.2 → 0.27.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 +13 -69
- package/dist/cli.js +19 -7
- package/dist/commands/ai-setup.d.ts +8 -2
- package/dist/commands/ai-setup.js +153 -347
- package/dist/commands/create-requirement-document.js +2 -1
- package/dist/commands/init.js +1 -1
- package/dist/commands/mcp.d.ts +8 -2
- package/dist/commands/mcp.js +17 -6
- package/dist/commands/review-test.d.ts +5 -1
- package/dist/commands/review-test.js +101 -7
- package/dist/commands/style-check.d.ts +1 -0
- package/dist/commands/style-check.js +138 -13
- package/dist/convex.d.ts +1 -3
- package/dist/convex.js +3 -3
- package/dist/requirements/cloud-ai.d.ts +21 -8
- package/dist/requirements/cloud-ai.js +10 -8
- package/dist/requirements/style-guide-file.d.ts +21 -0
- package/dist/requirements/style-guide-file.js +30 -0
- package/dist/requirements/style-guide.d.ts +20 -22
- package/dist/requirements/style-guide.js +57 -35
- package/dist/schema/browser.d.ts +1 -1
- package/dist/schema/browser.js +4 -1
- package/dist/schema/parser-core.d.ts +28 -0
- package/dist/schema/parser-core.js +51 -12
- package/dist/templates/context-file-section.md +25 -22
- package/dist/utils/context-file.d.ts +7 -3
- package/dist/utils/context-file.js +10 -7
- package/dist/utils/project-settings.d.ts +1 -0
- package/dist/utils/project-settings.js +22 -0
- package/package.json +3 -4
- package/dist/mcp/convexClient.d.ts +0 -19
- package/dist/mcp/convexClient.js +0 -24
- package/dist/mcp/handlers/authoring.d.ts +0 -41
- package/dist/mcp/handlers/authoring.js +0 -113
- package/dist/mcp/handlers/debug.d.ts +0 -16
- package/dist/mcp/handlers/debug.js +0 -37
- package/dist/mcp/handlers/get.d.ts +0 -24
- package/dist/mcp/handlers/get.js +0 -69
- package/dist/mcp/handlers/index.d.ts +0 -28
- package/dist/mcp/handlers/index.js +0 -19
- package/dist/mcp/handlers/list.d.ts +0 -7
- package/dist/mcp/handlers/list.js +0 -43
- package/dist/mcp/handlers/push.d.ts +0 -26
- package/dist/mcp/handlers/push.js +0 -232
- package/dist/mcp/handlers/report.d.ts +0 -16
- package/dist/mcp/handlers/report.js +0 -134
- package/dist/mcp/handlers/review.d.ts +0 -52
- package/dist/mcp/handlers/review.js +0 -243
- package/dist/mcp/handlers/search.d.ts +0 -30
- package/dist/mcp/handlers/search.js +0 -58
- package/dist/mcp/handlers/test-mapping.d.ts +0 -39
- package/dist/mcp/handlers/test-mapping.js +0 -168
- package/dist/mcp/handlers/types.d.ts +0 -89
- package/dist/mcp/handlers/types.js +0 -52
- package/dist/mcp/index.d.ts +0 -45
- package/dist/mcp/index.js +0 -638
package/dist/mcp/index.js
DELETED
|
@@ -1,638 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { readFileSync } from "node:fs";
|
|
3
|
-
import { dirname, join } from "node:path";
|
|
4
|
-
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
6
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
7
|
-
import { CallToolRequestSchema, GetPromptRequestSchema, ListPromptsRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
8
|
-
import { loadAllRequirements, } from "../requirements/index.js";
|
|
9
|
-
import { discoverProjects, resolveProject, } from "../utils/project-discovery.js";
|
|
10
|
-
import { getCredentialsFromEnv } from "../utils/project-settings.js";
|
|
11
|
-
import { handleCreateRequirementDocument, handleDebugMcpEnvironment, handleGetRequirement, handleGetRequirementsByTest, handleGetTestsByRequirement, handleList, handlePushRequirements, handleReport, handleReviewTest, handleSearchRequirements, handleStyleCheck, handleValidateRequirements, } from "./handlers/index.js";
|
|
12
|
-
// Read version from package.json
|
|
13
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
-
const __dirname = dirname(__filename);
|
|
15
|
-
const packageJson = JSON.parse(readFileSync(join(__dirname, "../../package.json"), "utf-8"));
|
|
16
|
-
const VERSION = packageJson.version;
|
|
17
|
-
// Get project paths from environment variables
|
|
18
|
-
// Supports both PROJ_* pattern (Antigravity) and REQUIREMENTS_DIR fallback
|
|
19
|
-
function getProjectPathsFromEnv() {
|
|
20
|
-
const projects = new Map();
|
|
21
|
-
// Check for PROJ_* env vars (project ID -> path mapping)
|
|
22
|
-
for (const [key, value] of Object.entries(process.env)) {
|
|
23
|
-
if (key.startsWith("PROJ_") && value) {
|
|
24
|
-
const projectId = key.substring(5); // Remove 'PROJ_' prefix
|
|
25
|
-
projects.set(projectId, value);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
return projects;
|
|
29
|
-
}
|
|
30
|
-
// Get workspace root from environment or default to cwd
|
|
31
|
-
const WORKSPACE_ROOT = process.env.REQUIREMENTS_DIR || process.cwd();
|
|
32
|
-
const PROJECT_PATHS = getProjectPathsFromEnv();
|
|
33
|
-
// Parse --auth-from-env flag for CI/CD environments
|
|
34
|
-
// When set, credentials are read from DOTREQ_PROJECT_ID and DOTREQ_PROJECT_SECRET env vars
|
|
35
|
-
const USE_ENV_AUTH = process.argv.includes("--auth-from-env");
|
|
36
|
-
let cachedDiscoveryResult = null;
|
|
37
|
-
// Requirements are loaded fresh on every call. A long-lived in-memory cache
|
|
38
|
-
// silently served stale data when .requirements/*.md files were created or
|
|
39
|
-
// edited mid-session, which is the primary authoring workflow the MCP server
|
|
40
|
-
// is meant to support. If this ever becomes a measured performance concern,
|
|
41
|
-
// invalidate via directory mtime rather than reintroducing a lifetime cache.
|
|
42
|
-
async function getRequirements(projectId) {
|
|
43
|
-
const project = await getProjectFromDiscovery(projectId);
|
|
44
|
-
const { flattened } = await loadAllRequirements(project.path);
|
|
45
|
-
return flattened;
|
|
46
|
-
}
|
|
47
|
-
async function getProjectFromDiscovery(projectId) {
|
|
48
|
-
const { isConfiguredProject } = await import("../utils/project-discovery.js");
|
|
49
|
-
// If --auth-from-env flag is set, use credentials from environment variables
|
|
50
|
-
// This is the explicit opt-in for CI/CD environments
|
|
51
|
-
if (USE_ENV_AUTH) {
|
|
52
|
-
const envCredentials = getCredentialsFromEnv();
|
|
53
|
-
if (!envCredentials) {
|
|
54
|
-
throw new Error("--auth-from-env flag requires DOTREQ_PROJECT_ID and DOTREQ_PROJECT_SECRET environment variables to be set");
|
|
55
|
-
}
|
|
56
|
-
// Return a synthetic project using env credentials and workspace root
|
|
57
|
-
return {
|
|
58
|
-
path: WORKSPACE_ROOT,
|
|
59
|
-
projectId: envCredentials.projectId,
|
|
60
|
-
projectSecret: envCredentials.projectSecret,
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
// If we have PROJ_* env vars, use those instead of filesystem discovery
|
|
64
|
-
if (PROJECT_PATHS.size > 0) {
|
|
65
|
-
const projects = [];
|
|
66
|
-
// Validate all env var projects
|
|
67
|
-
for (const [, path] of PROJECT_PATHS.entries()) {
|
|
68
|
-
const project = isConfiguredProject(path);
|
|
69
|
-
if (project) {
|
|
70
|
-
projects.push(project);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
// Build discovery result from env var projects
|
|
74
|
-
if (projects.length === 0) {
|
|
75
|
-
cachedDiscoveryResult = {
|
|
76
|
-
type: "none",
|
|
77
|
-
message: "No valid dotrequirements projects found in PROJ_* environment variables",
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
else if (projects.length === 1) {
|
|
81
|
-
cachedDiscoveryResult = { type: "single", project: projects[0] };
|
|
82
|
-
}
|
|
83
|
-
else {
|
|
84
|
-
cachedDiscoveryResult = { type: "multiple", projects };
|
|
85
|
-
}
|
|
86
|
-
return resolveProject(cachedDiscoveryResult, projectId);
|
|
87
|
-
}
|
|
88
|
-
// Fall back to filesystem discovery if no PROJ_* env vars
|
|
89
|
-
if (!cachedDiscoveryResult) {
|
|
90
|
-
cachedDiscoveryResult = await discoverProjects(WORKSPACE_ROOT);
|
|
91
|
-
}
|
|
92
|
-
return resolveProject(cachedDiscoveryResult, projectId);
|
|
93
|
-
}
|
|
94
|
-
// Invalidate the project-discovery cache. Requirements are no longer cached,
|
|
95
|
-
// so this only resets discovery state. Exported for testing.
|
|
96
|
-
export function invalidateCache() {
|
|
97
|
-
cachedDiscoveryResult = null;
|
|
98
|
-
}
|
|
99
|
-
// Tool definitions
|
|
100
|
-
const tools = [
|
|
101
|
-
{
|
|
102
|
-
name: "debug_mcp_environment",
|
|
103
|
-
description: "DEBUG TOOL: Returns diagnostic information about the MCP server environment (working directory, env vars, etc.)",
|
|
104
|
-
inputSchema: {
|
|
105
|
-
type: "object",
|
|
106
|
-
properties: {},
|
|
107
|
-
required: [],
|
|
108
|
-
},
|
|
109
|
-
},
|
|
110
|
-
{
|
|
111
|
-
name: "search_requirements",
|
|
112
|
-
description: "Search requirements by text or regex query. Searches requirement IDs, content, and labels. Returns matching requirements with their full context.",
|
|
113
|
-
inputSchema: {
|
|
114
|
-
type: "object",
|
|
115
|
-
properties: {
|
|
116
|
-
query: {
|
|
117
|
-
type: "string",
|
|
118
|
-
description: "Text or regex pattern to search for in requirement IDs, content, or labels",
|
|
119
|
-
},
|
|
120
|
-
useRegex: {
|
|
121
|
-
type: "boolean",
|
|
122
|
-
description: "If true, treat query as a regular expression (default: false)",
|
|
123
|
-
},
|
|
124
|
-
projectId: {
|
|
125
|
-
type: "string",
|
|
126
|
-
description: "Optional: Specify which project to search (required when multiple projects exist)",
|
|
127
|
-
},
|
|
128
|
-
},
|
|
129
|
-
required: ["query"],
|
|
130
|
-
},
|
|
131
|
-
},
|
|
132
|
-
{
|
|
133
|
-
name: "get_requirement",
|
|
134
|
-
description: 'Get a requirement and all its children, including test coverage information. Accepts any requirement path (e.g., "REQ-123" returns the root and all children, "REQ-123.0" returns that node and its children). Shows which tests reference this requirement and includes the test code.',
|
|
135
|
-
inputSchema: {
|
|
136
|
-
type: "object",
|
|
137
|
-
properties: {
|
|
138
|
-
id: {
|
|
139
|
-
type: "string",
|
|
140
|
-
description: 'The requirement ID or path (e.g., "REQ-123" for root, "REQ-123.0" for child)',
|
|
141
|
-
},
|
|
142
|
-
projectId: {
|
|
143
|
-
type: "string",
|
|
144
|
-
description: "Optional: Specify which project to search (required when multiple projects exist)",
|
|
145
|
-
},
|
|
146
|
-
},
|
|
147
|
-
required: ["id"],
|
|
148
|
-
},
|
|
149
|
-
},
|
|
150
|
-
{
|
|
151
|
-
name: "list_requirements",
|
|
152
|
-
description: "List requirements in the workspace. Defaults to every root requirement (summary of IDs and content with child counts). Set untested to true to filter to only the root requirements that have no requirement() references in the codebase.",
|
|
153
|
-
inputSchema: {
|
|
154
|
-
type: "object",
|
|
155
|
-
properties: {
|
|
156
|
-
untested: {
|
|
157
|
-
type: "boolean",
|
|
158
|
-
description: "When true, return only root requirements that have no test references (default: false)",
|
|
159
|
-
},
|
|
160
|
-
projectId: {
|
|
161
|
-
type: "string",
|
|
162
|
-
description: "Optional: Specify which project to search (required when multiple projects exist)",
|
|
163
|
-
},
|
|
164
|
-
},
|
|
165
|
-
required: [],
|
|
166
|
-
},
|
|
167
|
-
},
|
|
168
|
-
{
|
|
169
|
-
name: "get_requirements_by_test",
|
|
170
|
-
description: "Get the semantic meaning of requirement() references in a test file. Returns requirement content with line numbers where they are referenced.",
|
|
171
|
-
inputSchema: {
|
|
172
|
-
type: "object",
|
|
173
|
-
properties: {
|
|
174
|
-
testFile: {
|
|
175
|
-
type: "string",
|
|
176
|
-
description: "Path to the test file to analyze",
|
|
177
|
-
},
|
|
178
|
-
projectId: {
|
|
179
|
-
type: "string",
|
|
180
|
-
description: "Optional: Specify which project to search (required when multiple projects exist)",
|
|
181
|
-
},
|
|
182
|
-
},
|
|
183
|
-
required: ["testFile"],
|
|
184
|
-
},
|
|
185
|
-
},
|
|
186
|
-
{
|
|
187
|
-
name: "get_tests_by_requirement",
|
|
188
|
-
description: "Show which requirements from a requirements file have test coverage. Returns a coverage report showing which requirements are tested and where the tests are located. This is the inverse of get_requirements_by_test.",
|
|
189
|
-
inputSchema: {
|
|
190
|
-
type: "object",
|
|
191
|
-
properties: {
|
|
192
|
-
requirementsFile: {
|
|
193
|
-
type: "string",
|
|
194
|
-
description: 'Path to the requirements file (e.g., ".requirements/auth.requirements.md")',
|
|
195
|
-
},
|
|
196
|
-
projectId: {
|
|
197
|
-
type: "string",
|
|
198
|
-
description: "Optional: Specify which project to search (required when multiple projects exist)",
|
|
199
|
-
},
|
|
200
|
-
},
|
|
201
|
-
required: ["requirementsFile"],
|
|
202
|
-
},
|
|
203
|
-
},
|
|
204
|
-
{
|
|
205
|
-
name: "report_coverage",
|
|
206
|
-
description: "Display test coverage for the project's requirements. Defaults to local source (most recent test run on this machine, requires `dotrequirements harness prepare` to have run); set source to cloud to query the dot•requirements cloud-persisted record (requires project to be linked via `dotrequirements link`). Optional requirementKey scopes to a single requirement; branch / sinceTimestamp filter cloud-source results.",
|
|
207
|
-
inputSchema: {
|
|
208
|
-
type: "object",
|
|
209
|
-
properties: {
|
|
210
|
-
source: {
|
|
211
|
-
type: "string",
|
|
212
|
-
enum: ["local", "cloud"],
|
|
213
|
-
description: "Where to read coverage from (default: local)",
|
|
214
|
-
},
|
|
215
|
-
requirementKey: {
|
|
216
|
-
type: "string",
|
|
217
|
-
description: 'Optional: scope to a single requirement (e.g., "REQ-123"). For local source, also includes that requirement\'s children.',
|
|
218
|
-
},
|
|
219
|
-
branch: {
|
|
220
|
-
type: "string",
|
|
221
|
-
description: 'Cloud-only: filter coverage to a specific git branch (e.g., "main")',
|
|
222
|
-
},
|
|
223
|
-
sinceTimestamp: {
|
|
224
|
-
type: "number",
|
|
225
|
-
description: "Cloud-only: only include coverage recorded after this timestamp (milliseconds since epoch)",
|
|
226
|
-
},
|
|
227
|
-
projectId: {
|
|
228
|
-
type: "string",
|
|
229
|
-
description: "Optional: specify which project to use (required when multiple projects exist)",
|
|
230
|
-
},
|
|
231
|
-
},
|
|
232
|
-
required: [],
|
|
233
|
-
},
|
|
234
|
-
},
|
|
235
|
-
{
|
|
236
|
-
name: "create_requirement_document",
|
|
237
|
-
description: "Returns a well-formatted Markdown template that demonstrates the requirements file format. Use this to understand the format before creating requirements files. AI should use this template as a reference, then use filesystem tools (Read/Write) to create actual files.",
|
|
238
|
-
inputSchema: {
|
|
239
|
-
type: "object",
|
|
240
|
-
properties: {
|
|
241
|
-
filePath: {
|
|
242
|
-
type: "string",
|
|
243
|
-
description: 'Optional: Suggested file path for documentation purposes (e.g., ".requirements/auth.requirements.md")',
|
|
244
|
-
},
|
|
245
|
-
},
|
|
246
|
-
required: [],
|
|
247
|
-
},
|
|
248
|
-
},
|
|
249
|
-
{
|
|
250
|
-
name: "validate_requirements",
|
|
251
|
-
description: "Validate a requirements Markdown file's schema. Returns detailed error messages on syntax problems. Works offline (no network/auth required). Use this to verify files before pushing.",
|
|
252
|
-
inputSchema: {
|
|
253
|
-
type: "object",
|
|
254
|
-
properties: {
|
|
255
|
-
filePath: {
|
|
256
|
-
type: "string",
|
|
257
|
-
description: 'Path to the Markdown file to validate (e.g., ".requirements/auth.requirements.md")',
|
|
258
|
-
},
|
|
259
|
-
},
|
|
260
|
-
required: ["filePath"],
|
|
261
|
-
},
|
|
262
|
-
},
|
|
263
|
-
{
|
|
264
|
-
name: "push_requirements",
|
|
265
|
-
description: "Push local requirements from .requirements/ directory to dot•requirements cloud. First call returns diff summary for user review. Second call with confirmed=true executes the push. Requires project to be linked to cloud (run `dotrequirements link`).",
|
|
266
|
-
inputSchema: {
|
|
267
|
-
type: "object",
|
|
268
|
-
properties: {
|
|
269
|
-
filePath: {
|
|
270
|
-
type: "string",
|
|
271
|
-
description: 'Optional: Push specific file only (e.g., ".requirements/auth.requirements.md"). If omitted, pushes all files.',
|
|
272
|
-
},
|
|
273
|
-
confirmed: {
|
|
274
|
-
type: "boolean",
|
|
275
|
-
description: "Set to true to execute the push after reviewing the diff. First call should omit this.",
|
|
276
|
-
},
|
|
277
|
-
projectId: {
|
|
278
|
-
type: "string",
|
|
279
|
-
description: "Optional: Specify which project to use (required when multiple projects exist)",
|
|
280
|
-
},
|
|
281
|
-
},
|
|
282
|
-
required: [],
|
|
283
|
-
},
|
|
284
|
-
},
|
|
285
|
-
{
|
|
286
|
-
name: "style_check",
|
|
287
|
-
description: "Check requirements files or test files for style issues and best practices. Uses AI to provide actionable feedback on writing style, clarity, and conventions. Supports requirements files (*.requirements.md) and test files (*.test.*, *.spec.*). For requirements files, you can optionally specify requirement keys to check only those requirements instead of the entire file. Requires project to be linked to cloud (run `dotrequirements link`).",
|
|
288
|
-
inputSchema: {
|
|
289
|
-
type: "object",
|
|
290
|
-
properties: {
|
|
291
|
-
filePath: {
|
|
292
|
-
type: "string",
|
|
293
|
-
description: 'Path to the file to check (e.g., ".requirements/auth.requirements.md" or "src/auth.test.ts")',
|
|
294
|
-
},
|
|
295
|
-
requirementKeys: {
|
|
296
|
-
type: "array",
|
|
297
|
-
items: { type: "string" },
|
|
298
|
-
description: 'Optional: Array of requirement keys to check (e.g., ["AUTH-1", "AUTH-2"]). Only valid for requirements files (*.requirements.md). When provided, only these requirements are checked instead of the entire file.',
|
|
299
|
-
},
|
|
300
|
-
model: {
|
|
301
|
-
type: "string",
|
|
302
|
-
description: 'Optional: AI model to use for style checking (default: "anthropic/claude-haiku-4.5"). Supported models: "anthropic/claude-haiku-4.5", "google/gemini-3-flash"',
|
|
303
|
-
},
|
|
304
|
-
projectId: {
|
|
305
|
-
type: "string",
|
|
306
|
-
description: "Optional: Specify which project to use (required when multiple projects exist)",
|
|
307
|
-
},
|
|
308
|
-
},
|
|
309
|
-
required: ["filePath"],
|
|
310
|
-
},
|
|
311
|
-
},
|
|
312
|
-
{
|
|
313
|
-
name: "review_test",
|
|
314
|
-
description: "Comprehensively review a test file for both style and semantic correctness. Checks if tests actually validate what the requirements specify (not just style). Loads referenced requirements and validates that test setup, actions, and assertions match requirement preconditions, triggers, and outcomes. Requires project to be linked to cloud (run `dotrequirements link`).",
|
|
315
|
-
inputSchema: {
|
|
316
|
-
type: "object",
|
|
317
|
-
properties: {
|
|
318
|
-
testFilePath: {
|
|
319
|
-
type: "string",
|
|
320
|
-
description: 'Path to the test file to review (e.g., "src/auth.test.ts")',
|
|
321
|
-
},
|
|
322
|
-
projectId: {
|
|
323
|
-
type: "string",
|
|
324
|
-
description: "Optional: Specify which project to use (required when multiple projects exist)",
|
|
325
|
-
},
|
|
326
|
-
},
|
|
327
|
-
required: ["testFilePath"],
|
|
328
|
-
},
|
|
329
|
-
},
|
|
330
|
-
];
|
|
331
|
-
// Prompt definitions
|
|
332
|
-
const prompts = [
|
|
333
|
-
{
|
|
334
|
-
name: "capture-requirements",
|
|
335
|
-
description: "Guide the user through capturing requirements for a new feature before implementation",
|
|
336
|
-
},
|
|
337
|
-
{
|
|
338
|
-
name: "write-tests",
|
|
339
|
-
description: "Guide writing tests that reference and validate requirements",
|
|
340
|
-
},
|
|
341
|
-
];
|
|
342
|
-
// Create server (exported for testing with InMemoryTransport)
|
|
343
|
-
export const server = new Server({
|
|
344
|
-
name: "dotrequirements",
|
|
345
|
-
version: VERSION,
|
|
346
|
-
}, {
|
|
347
|
-
capabilities: {
|
|
348
|
-
tools: {},
|
|
349
|
-
prompts: {},
|
|
350
|
-
},
|
|
351
|
-
});
|
|
352
|
-
// Handle list tools
|
|
353
|
-
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
354
|
-
return { tools };
|
|
355
|
-
});
|
|
356
|
-
// Handle list prompts
|
|
357
|
-
server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
358
|
-
return { prompts };
|
|
359
|
-
});
|
|
360
|
-
// Handle get prompt
|
|
361
|
-
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
362
|
-
const { name } = request.params;
|
|
363
|
-
if (name === "capture-requirements") {
|
|
364
|
-
return {
|
|
365
|
-
messages: [
|
|
366
|
-
{
|
|
367
|
-
role: "user",
|
|
368
|
-
content: {
|
|
369
|
-
type: "text",
|
|
370
|
-
text: `# Capture Requirements for New Feature
|
|
371
|
-
|
|
372
|
-
Let's document what this feature should do before building it.
|
|
373
|
-
|
|
374
|
-
## Questions to Ask
|
|
375
|
-
|
|
376
|
-
1. What problem does this solve for users?
|
|
377
|
-
2. What are the key behaviors we need to support?
|
|
378
|
-
3. Are there edge cases or error conditions to handle?
|
|
379
|
-
4. How will we know it works correctly?
|
|
380
|
-
|
|
381
|
-
## Workflow
|
|
382
|
-
|
|
383
|
-
### Step 1: Create Requirements Document
|
|
384
|
-
|
|
385
|
-
Use \`create_requirement_document\` to get a template with:
|
|
386
|
-
- Format guidance (discovers existing patterns in your codebase)
|
|
387
|
-
- Style principles for writing clear, testable requirements
|
|
388
|
-
- Examples of well-written requirements
|
|
389
|
-
|
|
390
|
-
Fill in:
|
|
391
|
-
- Project ID (from .env.local)
|
|
392
|
-
- Document title
|
|
393
|
-
- Requirement IDs, descriptions, and expected behaviors
|
|
394
|
-
|
|
395
|
-
### Step 2: Validate & Refine (Optional)
|
|
396
|
-
|
|
397
|
-
- \`validate_requirements\` - Check syntax is correct (works offline)
|
|
398
|
-
- \`style_check\` - Get AI-powered feedback on writing quality and clarity
|
|
399
|
-
|
|
400
|
-
### Step 3: Push to Cloud
|
|
401
|
-
|
|
402
|
-
- \`push_requirements\` - Sync to dot•requirements cloud
|
|
403
|
-
- First call shows a diff preview
|
|
404
|
-
- Second call with \`confirmed: true\` executes the push
|
|
405
|
-
|
|
406
|
-
### Step 4: Implement & Test
|
|
407
|
-
|
|
408
|
-
After capturing requirements:
|
|
409
|
-
1. Implement the feature
|
|
410
|
-
2. Write tests that reference requirements using \`requirement('REQ-ID')\`
|
|
411
|
-
3. See your project's context file (CLAUDE.md/AGENTS.md) for test structure guidance`,
|
|
412
|
-
},
|
|
413
|
-
},
|
|
414
|
-
],
|
|
415
|
-
};
|
|
416
|
-
}
|
|
417
|
-
if (name === "write-tests") {
|
|
418
|
-
return {
|
|
419
|
-
messages: [
|
|
420
|
-
{
|
|
421
|
-
role: "user",
|
|
422
|
-
content: {
|
|
423
|
-
type: "text",
|
|
424
|
-
text: `# Write Tests for Requirements
|
|
425
|
-
|
|
426
|
-
Let's write tests that validate the requirements.
|
|
427
|
-
|
|
428
|
-
## Core Principles
|
|
429
|
-
|
|
430
|
-
### 1. Use requirement() AS the test description
|
|
431
|
-
|
|
432
|
-
\`\`\`typescript
|
|
433
|
-
// ✅ Good - requirement() is the description
|
|
434
|
-
test(requirement('AUTH-1'), () => { /* test code */ });
|
|
435
|
-
describe(requirement('LOGIN-1.given'), () => { /* setup */ });
|
|
436
|
-
it(requirement('LOGIN-1.then'), () => { /* assert */ });
|
|
437
|
-
|
|
438
|
-
// ❌ Bad - requirement() in body or comments
|
|
439
|
-
test("user can log in", () => { requirement('AUTH-1'); /* test code */ });
|
|
440
|
-
// LOGIN-1: User can log in
|
|
441
|
-
\`\`\`
|
|
442
|
-
|
|
443
|
-
### 2. Structure tests to match requirement hierarchy
|
|
444
|
-
|
|
445
|
-
For structured requirements, nest describe/it blocks:
|
|
446
|
-
|
|
447
|
-
\`\`\`typescript
|
|
448
|
-
describe(requirement('LOGIN-1'), () => {
|
|
449
|
-
describe(requirement('LOGIN-1.given'), () => {
|
|
450
|
-
// Arrange: Set up preconditions
|
|
451
|
-
});
|
|
452
|
-
|
|
453
|
-
describe(requirement('LOGIN-1.when'), () => {
|
|
454
|
-
// Act: Trigger the behavior
|
|
455
|
-
|
|
456
|
-
it(requirement('LOGIN-1.then'), () => {
|
|
457
|
-
// Assert: Verify outcome
|
|
458
|
-
});
|
|
459
|
-
});
|
|
460
|
-
});
|
|
461
|
-
\`\`\`
|
|
462
|
-
|
|
463
|
-
For simple requirements, one test block is fine:
|
|
464
|
-
|
|
465
|
-
\`\`\`typescript
|
|
466
|
-
test(requirement('AUTH-1'), () => {
|
|
467
|
-
// arrange, act, assert all in one
|
|
468
|
-
});
|
|
469
|
-
\`\`\`
|
|
470
|
-
|
|
471
|
-
### 3. Comments describe the TEST, not the requirement
|
|
472
|
-
|
|
473
|
-
\`\`\`typescript
|
|
474
|
-
// ✅ Good - comment explains test implementation
|
|
475
|
-
describe(requirement('REQ-1.0'), () => {
|
|
476
|
-
// Mock user with valid credentials
|
|
477
|
-
});
|
|
478
|
-
|
|
479
|
-
// ❌ Bad - verbatim copy of requirement text
|
|
480
|
-
describe(requirement('REQ-1.0'), () => {
|
|
481
|
-
// 0. When a registered user provides valid credentials, they are authenticated
|
|
482
|
-
});
|
|
483
|
-
\`\`\`
|
|
484
|
-
|
|
485
|
-
### 4. NEVER document coverage in comments
|
|
486
|
-
|
|
487
|
-
Our MCP tools track coverage automatically. Don't create a second source of truth.
|
|
488
|
-
|
|
489
|
-
\`\`\`typescript
|
|
490
|
-
// ❌ Bad - creates second source of truth
|
|
491
|
-
/**
|
|
492
|
-
* Requirements coverage:
|
|
493
|
-
* - AUTH-9.0: Backend creates project ✓
|
|
494
|
-
* - AUTH-9.1: CLI writes to .env.local ✓
|
|
495
|
-
*/
|
|
496
|
-
|
|
497
|
-
// ✅ Good - use MCP tools to check coverage
|
|
498
|
-
// Use get_requirement, get_requirements_by_test
|
|
499
|
-
\`\`\`
|
|
500
|
-
|
|
501
|
-
## Requirement Path Formats
|
|
502
|
-
|
|
503
|
-
- \`requirement('AUTH-1')\` - root requirement
|
|
504
|
-
- \`requirement('AUTH-1.0')\` - by numeric position
|
|
505
|
-
- \`requirement('AUTH-1.given')\` - by label (case-insensitive)
|
|
506
|
-
- \`requirement('AUTH-1.given#1')\` - disambiguate duplicate labels
|
|
507
|
-
- \`requirement('AUTH-1.then.and')\` - nested label path
|
|
508
|
-
|
|
509
|
-
## Workflow
|
|
510
|
-
|
|
511
|
-
After writing tests:
|
|
512
|
-
|
|
513
|
-
1. Run tests to ensure they don't throw errors
|
|
514
|
-
2. **ALWAYS run \`review_test\`** on the test file to validate tests actually check what requirements specify
|
|
515
|
-
3. Fix any issues identified by the review
|
|
516
|
-
|
|
517
|
-
## MCP Tools for Test Writing
|
|
518
|
-
|
|
519
|
-
- \`get_requirement\` - Get requirement tree with existing coverage
|
|
520
|
-
- \`get_requirements_by_test\` - See requirements referenced in a test file
|
|
521
|
-
- \`list_requirements\` (with \`untested: true\`) - Find requirements without tests
|
|
522
|
-
- \`review_test\` - Validate test actually checks what requirement specifies (run after writing tests)`,
|
|
523
|
-
},
|
|
524
|
-
},
|
|
525
|
-
],
|
|
526
|
-
};
|
|
527
|
-
}
|
|
528
|
-
throw new Error(`Unknown prompt: ${name}`);
|
|
529
|
-
});
|
|
530
|
-
/**
|
|
531
|
-
* MCP-ARGS-1: find required arguments (per the tool's inputSchema) that are
|
|
532
|
-
* missing from a call, so we can fail with a self-explaining error before the
|
|
533
|
-
* handler runs — a missing argument must never surface as an internal crash.
|
|
534
|
-
*/
|
|
535
|
-
function findMissingRequiredArgs(toolName, args) {
|
|
536
|
-
const tool = tools.find((t) => t.name === toolName);
|
|
537
|
-
if (!tool)
|
|
538
|
-
return undefined; // unknown tools get their own error downstream
|
|
539
|
-
const schema = tool.inputSchema;
|
|
540
|
-
const required = schema.required ?? [];
|
|
541
|
-
const missing = required.filter((key) => args?.[key] === undefined || args?.[key] === null);
|
|
542
|
-
if (missing.length === 0)
|
|
543
|
-
return undefined;
|
|
544
|
-
return { missing, accepted: Object.keys(schema.properties ?? {}) };
|
|
545
|
-
}
|
|
546
|
-
// Handle tool calls
|
|
547
|
-
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
548
|
-
const { name, arguments: args } = request.params;
|
|
549
|
-
// Refresh cache for each request to pick up changes
|
|
550
|
-
invalidateCache();
|
|
551
|
-
// MCP-ARGS-1.2: reject calls missing required arguments before dispatch
|
|
552
|
-
const argCheck = findMissingRequiredArgs(name, args);
|
|
553
|
-
if (argCheck) {
|
|
554
|
-
return {
|
|
555
|
-
content: [
|
|
556
|
-
{
|
|
557
|
-
type: "text",
|
|
558
|
-
text: `Missing required argument(s) for ${name}: ${argCheck.missing.join(", ")}. Accepted arguments: ${argCheck.accepted.join(", ")}.`,
|
|
559
|
-
},
|
|
560
|
-
],
|
|
561
|
-
isError: true,
|
|
562
|
-
};
|
|
563
|
-
}
|
|
564
|
-
// Create handler context for extracted handlers
|
|
565
|
-
const handlerContext = {
|
|
566
|
-
getRequirements,
|
|
567
|
-
getProjectFromDiscovery,
|
|
568
|
-
workspaceRoot: WORKSPACE_ROOT,
|
|
569
|
-
projectPaths: PROJECT_PATHS,
|
|
570
|
-
env: process.env,
|
|
571
|
-
};
|
|
572
|
-
try {
|
|
573
|
-
switch (name) {
|
|
574
|
-
case "debug_mcp_environment":
|
|
575
|
-
return handleDebugMcpEnvironment({}, handlerContext);
|
|
576
|
-
case "search_requirements":
|
|
577
|
-
return handleSearchRequirements(args, handlerContext);
|
|
578
|
-
case "get_requirement":
|
|
579
|
-
return handleGetRequirement(args, handlerContext);
|
|
580
|
-
case "list_requirements":
|
|
581
|
-
return handleList(args, handlerContext);
|
|
582
|
-
case "get_requirements_by_test":
|
|
583
|
-
return handleGetRequirementsByTest(args, handlerContext);
|
|
584
|
-
case "get_tests_by_requirement":
|
|
585
|
-
return handleGetTestsByRequirement(args, handlerContext);
|
|
586
|
-
case "report_coverage":
|
|
587
|
-
return handleReport(args, handlerContext);
|
|
588
|
-
case "create_requirement_document":
|
|
589
|
-
return handleCreateRequirementDocument(args, handlerContext);
|
|
590
|
-
case "validate_requirements":
|
|
591
|
-
return handleValidateRequirements(args, handlerContext);
|
|
592
|
-
case "push_requirements":
|
|
593
|
-
return handlePushRequirements(args, handlerContext);
|
|
594
|
-
case "style_check":
|
|
595
|
-
return handleStyleCheck(args, handlerContext, {
|
|
596
|
-
useEnvAuth: USE_ENV_AUTH,
|
|
597
|
-
apiBaseUrl: process.env.DOTREQUIREMENTS_API_URL,
|
|
598
|
-
});
|
|
599
|
-
case "review_test":
|
|
600
|
-
return handleReviewTest(args, handlerContext, { apiBaseUrl: process.env.DOTREQUIREMENTS_API_URL });
|
|
601
|
-
default:
|
|
602
|
-
return {
|
|
603
|
-
content: [
|
|
604
|
-
{
|
|
605
|
-
type: "text",
|
|
606
|
-
text: `Unknown tool: ${name}`,
|
|
607
|
-
},
|
|
608
|
-
],
|
|
609
|
-
isError: true,
|
|
610
|
-
};
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
catch (error) {
|
|
614
|
-
return {
|
|
615
|
-
content: [
|
|
616
|
-
{
|
|
617
|
-
type: "text",
|
|
618
|
-
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
619
|
-
},
|
|
620
|
-
],
|
|
621
|
-
isError: true,
|
|
622
|
-
};
|
|
623
|
-
}
|
|
624
|
-
});
|
|
625
|
-
// Start server (exported for CLI command to call directly)
|
|
626
|
-
export async function main() {
|
|
627
|
-
const transport = new StdioServerTransport();
|
|
628
|
-
await server.connect(transport);
|
|
629
|
-
console.error("dot•requirements MCP server running");
|
|
630
|
-
console.error("WORKSPACE_ROOT:", WORKSPACE_ROOT);
|
|
631
|
-
console.error("process.cwd():", process.cwd());
|
|
632
|
-
console.error("REQUIREMENTS_DIR env:", process.env.REQUIREMENTS_DIR);
|
|
633
|
-
}
|
|
634
|
-
// Only run when executed directly, not when imported (enables InMemoryTransport testing)
|
|
635
|
-
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
636
|
-
main().catch(console.error);
|
|
637
|
-
}
|
|
638
|
-
//# sourceMappingURL=index.js.map
|