@popoverai/dotrequirements 0.26.1 → 0.27.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 (84) hide show
  1. package/README.md +13 -69
  2. package/dist/cli.js +19 -7
  3. package/dist/codebase-to-spec/present.js +4 -5
  4. package/dist/codebase-to-spec/validate.js +3 -2
  5. package/dist/commands/acceptance-test.js +4 -2
  6. package/dist/commands/ai-setup.d.ts +8 -2
  7. package/dist/commands/ai-setup.js +154 -310
  8. package/dist/commands/get.js +6 -2
  9. package/dist/commands/init.js +8 -6
  10. package/dist/commands/link-resolution.d.ts +3 -1
  11. package/dist/commands/link-resolution.js +4 -2
  12. package/dist/commands/mcp.d.ts +8 -2
  13. package/dist/commands/mcp.js +17 -6
  14. package/dist/commands/pull.js +36 -3
  15. package/dist/commands/push.js +54 -16
  16. package/dist/commands/report.js +18 -3
  17. package/dist/commands/review-test.d.ts +5 -1
  18. package/dist/commands/review-test.js +117 -15
  19. package/dist/commands/style-check.d.ts +1 -0
  20. package/dist/commands/style-check.js +137 -13
  21. package/dist/commands/tests-for.js +13 -13
  22. package/dist/commands/validate.js +14 -14
  23. package/dist/convex.d.ts +1 -3
  24. package/dist/convex.js +3 -3
  25. package/dist/harness/cache.d.ts +19 -3
  26. package/dist/harness/cache.js +38 -12
  27. package/dist/harness/finalize.js +33 -1
  28. package/dist/harness/index.js +16 -9
  29. package/dist/harness/requirementsLoader.js +12 -0
  30. package/dist/harness/tracking.d.ts +17 -2
  31. package/dist/harness/tracking.js +83 -9
  32. package/dist/push/core.d.ts +50 -0
  33. package/dist/push/core.js +149 -11
  34. package/dist/push/index.d.ts +1 -1
  35. package/dist/push/index.js +1 -1
  36. package/dist/requirements/cloud-ai.d.ts +21 -8
  37. package/dist/requirements/cloud-ai.js +10 -8
  38. package/dist/requirements/cloud-coverage.d.ts +12 -2
  39. package/dist/requirements/cloud-coverage.js +30 -3
  40. package/dist/requirements/grep.d.ts +7 -2
  41. package/dist/requirements/grep.js +75 -47
  42. package/dist/schema/builder.d.ts +1 -1
  43. package/dist/schema/builder.js +13 -0
  44. package/dist/schema/conversions.d.ts +7 -2
  45. package/dist/schema/conversions.js +13 -4
  46. package/dist/schema/parser-core.d.ts +41 -0
  47. package/dist/schema/parser-core.js +113 -18
  48. package/dist/schema/parser.d.ts +8 -26
  49. package/dist/schema/parser.js +23 -251
  50. package/dist/schema/resolver.js +18 -8
  51. package/dist/templates/context-file-section.md +25 -22
  52. package/dist/utils/context-file.d.ts +7 -3
  53. package/dist/utils/context-file.js +10 -7
  54. package/dist/utils/env.js +17 -1
  55. package/dist/utils/oauth-flow.js +8 -0
  56. package/dist/utils/project-settings.d.ts +5 -0
  57. package/dist/utils/project-settings.js +36 -1
  58. package/package.json +3 -5
  59. package/dist/mcp/convexClient.d.ts +0 -19
  60. package/dist/mcp/convexClient.js +0 -24
  61. package/dist/mcp/handlers/authoring.d.ts +0 -41
  62. package/dist/mcp/handlers/authoring.js +0 -104
  63. package/dist/mcp/handlers/debug.d.ts +0 -16
  64. package/dist/mcp/handlers/debug.js +0 -37
  65. package/dist/mcp/handlers/get.d.ts +0 -24
  66. package/dist/mcp/handlers/get.js +0 -65
  67. package/dist/mcp/handlers/index.d.ts +0 -28
  68. package/dist/mcp/handlers/index.js +0 -19
  69. package/dist/mcp/handlers/list.d.ts +0 -7
  70. package/dist/mcp/handlers/list.js +0 -43
  71. package/dist/mcp/handlers/push.d.ts +0 -26
  72. package/dist/mcp/handlers/push.js +0 -186
  73. package/dist/mcp/handlers/report.d.ts +0 -16
  74. package/dist/mcp/handlers/report.js +0 -134
  75. package/dist/mcp/handlers/review.d.ts +0 -51
  76. package/dist/mcp/handlers/review.js +0 -200
  77. package/dist/mcp/handlers/search.d.ts +0 -30
  78. package/dist/mcp/handlers/search.js +0 -58
  79. package/dist/mcp/handlers/test-mapping.d.ts +0 -39
  80. package/dist/mcp/handlers/test-mapping.js +0 -133
  81. package/dist/mcp/handlers/types.d.ts +0 -75
  82. package/dist/mcp/handlers/types.js +0 -25
  83. package/dist/mcp/index.d.ts +0 -45
  84. package/dist/mcp/index.js +0 -634
package/dist/mcp/index.js DELETED
@@ -1,634 +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
- },
305
- required: ["filePath"],
306
- },
307
- },
308
- {
309
- name: "review_test",
310
- 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`).",
311
- inputSchema: {
312
- type: "object",
313
- properties: {
314
- testFilePath: {
315
- type: "string",
316
- description: 'Path to the test file to review (e.g., "src/auth.test.ts")',
317
- },
318
- projectId: {
319
- type: "string",
320
- description: "Optional: Specify which project to use (required when multiple projects exist)",
321
- },
322
- },
323
- required: ["testFilePath"],
324
- },
325
- },
326
- ];
327
- // Prompt definitions
328
- const prompts = [
329
- {
330
- name: "capture-requirements",
331
- description: "Guide the user through capturing requirements for a new feature before implementation",
332
- },
333
- {
334
- name: "write-tests",
335
- description: "Guide writing tests that reference and validate requirements",
336
- },
337
- ];
338
- // Create server (exported for testing with InMemoryTransport)
339
- export const server = new Server({
340
- name: "dotrequirements",
341
- version: VERSION,
342
- }, {
343
- capabilities: {
344
- tools: {},
345
- prompts: {},
346
- },
347
- });
348
- // Handle list tools
349
- server.setRequestHandler(ListToolsRequestSchema, async () => {
350
- return { tools };
351
- });
352
- // Handle list prompts
353
- server.setRequestHandler(ListPromptsRequestSchema, async () => {
354
- return { prompts };
355
- });
356
- // Handle get prompt
357
- server.setRequestHandler(GetPromptRequestSchema, async (request) => {
358
- const { name } = request.params;
359
- if (name === "capture-requirements") {
360
- return {
361
- messages: [
362
- {
363
- role: "user",
364
- content: {
365
- type: "text",
366
- text: `# Capture Requirements for New Feature
367
-
368
- Let's document what this feature should do before building it.
369
-
370
- ## Questions to Ask
371
-
372
- 1. What problem does this solve for users?
373
- 2. What are the key behaviors we need to support?
374
- 3. Are there edge cases or error conditions to handle?
375
- 4. How will we know it works correctly?
376
-
377
- ## Workflow
378
-
379
- ### Step 1: Create Requirements Document
380
-
381
- Use \`create_requirement_document\` to get a template with:
382
- - Format guidance (discovers existing patterns in your codebase)
383
- - Style principles for writing clear, testable requirements
384
- - Examples of well-written requirements
385
-
386
- Fill in:
387
- - Project ID (from .env.local)
388
- - Document title
389
- - Requirement IDs, descriptions, and expected behaviors
390
-
391
- ### Step 2: Validate & Refine (Optional)
392
-
393
- - \`validate_requirements\` - Check syntax is correct (works offline)
394
- - \`style_check\` - Get AI-powered feedback on writing quality and clarity
395
-
396
- ### Step 3: Push to Cloud
397
-
398
- - \`push_requirements\` - Sync to dot•requirements cloud
399
- - First call shows a diff preview
400
- - Second call with \`confirmed: true\` executes the push
401
-
402
- ### Step 4: Implement & Test
403
-
404
- After capturing requirements:
405
- 1. Implement the feature
406
- 2. Write tests that reference requirements using \`requirement('REQ-ID')\`
407
- 3. See your project's context file (CLAUDE.md/AGENTS.md) for test structure guidance`,
408
- },
409
- },
410
- ],
411
- };
412
- }
413
- if (name === "write-tests") {
414
- return {
415
- messages: [
416
- {
417
- role: "user",
418
- content: {
419
- type: "text",
420
- text: `# Write Tests for Requirements
421
-
422
- Let's write tests that validate the requirements.
423
-
424
- ## Core Principles
425
-
426
- ### 1. Use requirement() AS the test description
427
-
428
- \`\`\`typescript
429
- // ✅ Good - requirement() is the description
430
- test(requirement('AUTH-1'), () => { /* test code */ });
431
- describe(requirement('LOGIN-1.given'), () => { /* setup */ });
432
- it(requirement('LOGIN-1.then'), () => { /* assert */ });
433
-
434
- // ❌ Bad - requirement() in body or comments
435
- test("user can log in", () => { requirement('AUTH-1'); /* test code */ });
436
- // LOGIN-1: User can log in
437
- \`\`\`
438
-
439
- ### 2. Structure tests to match requirement hierarchy
440
-
441
- For structured requirements, nest describe/it blocks:
442
-
443
- \`\`\`typescript
444
- describe(requirement('LOGIN-1'), () => {
445
- describe(requirement('LOGIN-1.given'), () => {
446
- // Arrange: Set up preconditions
447
- });
448
-
449
- describe(requirement('LOGIN-1.when'), () => {
450
- // Act: Trigger the behavior
451
-
452
- it(requirement('LOGIN-1.then'), () => {
453
- // Assert: Verify outcome
454
- });
455
- });
456
- });
457
- \`\`\`
458
-
459
- For simple requirements, one test block is fine:
460
-
461
- \`\`\`typescript
462
- test(requirement('AUTH-1'), () => {
463
- // arrange, act, assert all in one
464
- });
465
- \`\`\`
466
-
467
- ### 3. Comments describe the TEST, not the requirement
468
-
469
- \`\`\`typescript
470
- // ✅ Good - comment explains test implementation
471
- describe(requirement('REQ-1.0'), () => {
472
- // Mock user with valid credentials
473
- });
474
-
475
- // ❌ Bad - verbatim copy of requirement text
476
- describe(requirement('REQ-1.0'), () => {
477
- // 0. When a registered user provides valid credentials, they are authenticated
478
- });
479
- \`\`\`
480
-
481
- ### 4. NEVER document coverage in comments
482
-
483
- Our MCP tools track coverage automatically. Don't create a second source of truth.
484
-
485
- \`\`\`typescript
486
- // ❌ Bad - creates second source of truth
487
- /**
488
- * Requirements coverage:
489
- * - AUTH-9.0: Backend creates project ✓
490
- * - AUTH-9.1: CLI writes to .env.local ✓
491
- */
492
-
493
- // ✅ Good - use MCP tools to check coverage
494
- // Use get_requirement, get_requirements_by_test
495
- \`\`\`
496
-
497
- ## Requirement Path Formats
498
-
499
- - \`requirement('AUTH-1')\` - root requirement
500
- - \`requirement('AUTH-1.0')\` - by numeric position
501
- - \`requirement('AUTH-1.given')\` - by label (case-insensitive)
502
- - \`requirement('AUTH-1.given#1')\` - disambiguate duplicate labels
503
- - \`requirement('AUTH-1.then.and')\` - nested label path
504
-
505
- ## Workflow
506
-
507
- After writing tests:
508
-
509
- 1. Run tests to ensure they don't throw errors
510
- 2. **ALWAYS run \`review_test\`** on the test file to validate tests actually check what requirements specify
511
- 3. Fix any issues identified by the review
512
-
513
- ## MCP Tools for Test Writing
514
-
515
- - \`get_requirement\` - Get requirement tree with existing coverage
516
- - \`get_requirements_by_test\` - See requirements referenced in a test file
517
- - \`list_requirements\` (with \`untested: true\`) - Find requirements without tests
518
- - \`review_test\` - Validate test actually checks what requirement specifies (run after writing tests)`,
519
- },
520
- },
521
- ],
522
- };
523
- }
524
- throw new Error(`Unknown prompt: ${name}`);
525
- });
526
- /**
527
- * MCP-ARGS-1: find required arguments (per the tool's inputSchema) that are
528
- * missing from a call, so we can fail with a self-explaining error before the
529
- * handler runs — a missing argument must never surface as an internal crash.
530
- */
531
- function findMissingRequiredArgs(toolName, args) {
532
- const tool = tools.find((t) => t.name === toolName);
533
- if (!tool)
534
- return undefined; // unknown tools get their own error downstream
535
- const schema = tool.inputSchema;
536
- const required = schema.required ?? [];
537
- const missing = required.filter((key) => args?.[key] === undefined || args?.[key] === null);
538
- if (missing.length === 0)
539
- return undefined;
540
- return { missing, accepted: Object.keys(schema.properties ?? {}) };
541
- }
542
- // Handle tool calls
543
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
544
- const { name, arguments: args } = request.params;
545
- // Refresh cache for each request to pick up changes
546
- invalidateCache();
547
- // MCP-ARGS-1.2: reject calls missing required arguments before dispatch
548
- const argCheck = findMissingRequiredArgs(name, args);
549
- if (argCheck) {
550
- return {
551
- content: [
552
- {
553
- type: "text",
554
- text: `Missing required argument(s) for ${name}: ${argCheck.missing.join(", ")}. Accepted arguments: ${argCheck.accepted.join(", ")}.`,
555
- },
556
- ],
557
- isError: true,
558
- };
559
- }
560
- // Create handler context for extracted handlers
561
- const handlerContext = {
562
- getRequirements,
563
- getProjectFromDiscovery,
564
- workspaceRoot: WORKSPACE_ROOT,
565
- projectPaths: PROJECT_PATHS,
566
- env: process.env,
567
- };
568
- try {
569
- switch (name) {
570
- case "debug_mcp_environment":
571
- return handleDebugMcpEnvironment({}, handlerContext);
572
- case "search_requirements":
573
- return handleSearchRequirements(args, handlerContext);
574
- case "get_requirement":
575
- return handleGetRequirement(args, handlerContext);
576
- case "list_requirements":
577
- return handleList(args, handlerContext);
578
- case "get_requirements_by_test":
579
- return handleGetRequirementsByTest(args, handlerContext);
580
- case "get_tests_by_requirement":
581
- return handleGetTestsByRequirement(args, handlerContext);
582
- case "report_coverage":
583
- return handleReport(args, handlerContext);
584
- case "create_requirement_document":
585
- return handleCreateRequirementDocument(args, handlerContext);
586
- case "validate_requirements":
587
- return handleValidateRequirements(args, handlerContext);
588
- case "push_requirements":
589
- return handlePushRequirements(args, handlerContext);
590
- case "style_check":
591
- return handleStyleCheck(args, handlerContext, {
592
- useEnvAuth: USE_ENV_AUTH,
593
- apiBaseUrl: process.env.DOTREQUIREMENTS_API_URL,
594
- });
595
- case "review_test":
596
- return handleReviewTest(args, handlerContext, { apiBaseUrl: process.env.DOTREQUIREMENTS_API_URL });
597
- default:
598
- return {
599
- content: [
600
- {
601
- type: "text",
602
- text: `Unknown tool: ${name}`,
603
- },
604
- ],
605
- isError: true,
606
- };
607
- }
608
- }
609
- catch (error) {
610
- return {
611
- content: [
612
- {
613
- type: "text",
614
- text: `Error: ${error instanceof Error ? error.message : String(error)}`,
615
- },
616
- ],
617
- isError: true,
618
- };
619
- }
620
- });
621
- // Start server (exported for CLI command to call directly)
622
- export async function main() {
623
- const transport = new StdioServerTransport();
624
- await server.connect(transport);
625
- console.error("dot•requirements MCP server running");
626
- console.error("WORKSPACE_ROOT:", WORKSPACE_ROOT);
627
- console.error("process.cwd():", process.cwd());
628
- console.error("REQUIREMENTS_DIR env:", process.env.REQUIREMENTS_DIR);
629
- }
630
- // Only run when executed directly, not when imported (enables InMemoryTransport testing)
631
- if (import.meta.url === `file://${process.argv[1]}`) {
632
- main().catch(console.error);
633
- }
634
- //# sourceMappingURL=index.js.map