@popoverai/dotrequirements 0.22.0 → 0.24.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.
- package/README.md +173 -23
- package/dist/cli.js +121 -61
- package/dist/codebase-to-spec/budget.d.ts +53 -0
- package/dist/codebase-to-spec/budget.js +80 -0
- package/dist/codebase-to-spec/cache.d.ts +49 -0
- package/dist/codebase-to-spec/cache.js +54 -0
- package/dist/codebase-to-spec/claude.d.ts +69 -0
- package/dist/codebase-to-spec/claude.js +126 -0
- package/dist/codebase-to-spec/compose.d.ts +49 -0
- package/dist/codebase-to-spec/compose.js +124 -0
- package/dist/codebase-to-spec/edit-loop.d.ts +54 -0
- package/dist/codebase-to-spec/edit-loop.js +195 -0
- package/dist/codebase-to-spec/editor.d.ts +54 -0
- package/dist/codebase-to-spec/editor.js +74 -0
- package/dist/codebase-to-spec/exit-codes.d.ts +40 -0
- package/dist/codebase-to-spec/exit-codes.js +58 -0
- package/dist/codebase-to-spec/fan-out.d.ts +63 -0
- package/dist/codebase-to-spec/fan-out.js +215 -0
- package/dist/codebase-to-spec/interactive.d.ts +30 -0
- package/dist/codebase-to-spec/interactive.js +48 -0
- package/dist/codebase-to-spec/outline-review-loop.d.ts +51 -0
- package/dist/codebase-to-spec/outline-review-loop.js +187 -0
- package/dist/codebase-to-spec/pack.d.ts +51 -0
- package/dist/codebase-to-spec/pack.js +127 -0
- package/dist/codebase-to-spec/planner.d.ts +41 -0
- package/dist/codebase-to-spec/planner.js +76 -0
- package/dist/codebase-to-spec/present.d.ts +94 -0
- package/dist/codebase-to-spec/present.js +288 -0
- package/dist/codebase-to-spec/progress.d.ts +33 -0
- package/dist/codebase-to-spec/progress.js +28 -0
- package/dist/codebase-to-spec/prompts/editor.d.ts +13 -0
- package/dist/codebase-to-spec/prompts/editor.js +57 -0
- package/dist/codebase-to-spec/prompts/outline-reviewer.d.ts +12 -0
- package/dist/codebase-to-spec/prompts/outline-reviewer.js +87 -0
- package/dist/codebase-to-spec/prompts/planner-apply.d.ts +12 -0
- package/dist/codebase-to-spec/prompts/planner-apply.js +32 -0
- package/dist/codebase-to-spec/prompts/planner-initial.d.ts +11 -0
- package/dist/codebase-to-spec/prompts/planner-initial.js +125 -0
- package/dist/codebase-to-spec/prompts/planner-revise.d.ts +14 -0
- package/dist/codebase-to-spec/prompts/planner-revise.js +60 -0
- package/dist/codebase-to-spec/prompts/spec-reviewer.d.ts +16 -0
- package/dist/codebase-to-spec/prompts/spec-reviewer.js +96 -0
- package/dist/codebase-to-spec/prompts/specifier.d.ts +12 -0
- package/dist/codebase-to-spec/prompts/specifier.js +100 -0
- package/dist/codebase-to-spec/prompts/style-check.d.ts +12 -0
- package/dist/codebase-to-spec/prompts/style-check.js +78 -0
- package/dist/codebase-to-spec/schemas.d.ts +257 -0
- package/dist/codebase-to-spec/schemas.js +183 -0
- package/dist/codebase-to-spec/skill-install.d.ts +57 -0
- package/dist/codebase-to-spec/skill-install.js +79 -0
- package/dist/codebase-to-spec/slice.d.ts +49 -0
- package/dist/codebase-to-spec/slice.js +111 -0
- package/dist/codebase-to-spec/specifier.d.ts +60 -0
- package/dist/codebase-to-spec/specifier.js +79 -0
- package/dist/codebase-to-spec/style-check.d.ts +29 -0
- package/dist/codebase-to-spec/style-check.js +33 -0
- package/dist/codebase-to-spec/summary.d.ts +51 -0
- package/dist/codebase-to-spec/summary.js +183 -0
- package/dist/codebase-to-spec/validate.d.ts +46 -0
- package/dist/codebase-to-spec/validate.js +130 -0
- package/dist/commands/acceptance-test.d.ts +6 -0
- package/dist/commands/acceptance-test.js +212 -0
- package/dist/commands/ai-setup.d.ts +5 -0
- package/dist/commands/ai-setup.js +441 -0
- package/dist/commands/browsertest.d.ts +0 -1
- package/dist/commands/browsertest.js +51 -26
- package/dist/commands/codebase-to-spec/compose.d.ts +14 -0
- package/dist/commands/codebase-to-spec/compose.js +57 -0
- package/dist/commands/codebase-to-spec/edit-loop.d.ts +16 -0
- package/dist/commands/codebase-to-spec/edit-loop.js +83 -0
- package/dist/commands/codebase-to-spec/fan-out.d.ts +19 -0
- package/dist/commands/codebase-to-spec/fan-out.js +77 -0
- package/dist/commands/codebase-to-spec/index.d.ts +9 -0
- package/dist/commands/codebase-to-spec/index.js +135 -0
- package/dist/commands/codebase-to-spec/pack.d.ts +22 -0
- package/dist/commands/codebase-to-spec/pack.js +76 -0
- package/dist/commands/codebase-to-spec/plan-loop.d.ts +26 -0
- package/dist/commands/codebase-to-spec/plan-loop.js +105 -0
- package/dist/commands/codebase-to-spec/present.d.ts +21 -0
- package/dist/commands/codebase-to-spec/present.js +92 -0
- package/dist/commands/codebase-to-spec/run.d.ts +20 -0
- package/dist/commands/codebase-to-spec/run.js +85 -0
- package/dist/commands/codebase-to-spec/skill-install.d.ts +20 -0
- package/dist/commands/codebase-to-spec/skill-install.js +51 -0
- package/dist/commands/codebase-to-spec/specify-area.d.ts +18 -0
- package/dist/commands/codebase-to-spec/specify-area.js +82 -0
- package/dist/commands/codebase-to-spec/style-check.d.ts +15 -0
- package/dist/commands/codebase-to-spec/style-check.js +42 -0
- package/dist/commands/codebase-to-spec/validate.d.ts +18 -0
- package/dist/commands/codebase-to-spec/validate.js +38 -0
- package/dist/commands/create-requirement-document.d.ts +2 -0
- package/dist/commands/create-requirement-document.js +41 -0
- package/dist/commands/finalize.js +7 -7
- package/dist/commands/get.d.ts +2 -0
- package/dist/commands/get.js +55 -0
- package/dist/commands/init.js +132 -117
- package/dist/commands/link.js +27 -27
- package/dist/commands/list.d.ts +6 -0
- package/dist/commands/list.js +43 -0
- package/dist/commands/mcp-setup.js +159 -149
- package/dist/commands/mcp.js +1 -1
- package/dist/commands/prepare.js +4 -4
- package/dist/commands/pull.js +116 -121
- package/dist/commands/push.js +106 -112
- package/dist/commands/report.d.ts +6 -2
- package/dist/commands/report.js +177 -122
- package/dist/commands/requirements-for.d.ts +2 -0
- package/dist/commands/requirements-for.js +29 -0
- package/dist/commands/review-test.d.ts +2 -0
- package/dist/commands/review-test.js +75 -0
- package/dist/commands/search.d.ts +6 -0
- package/dist/commands/search.js +39 -0
- package/dist/commands/style-check.d.ts +7 -0
- package/dist/commands/style-check.js +75 -0
- package/dist/commands/test.js +53 -59
- package/dist/commands/tests-for.d.ts +2 -0
- package/dist/commands/tests-for.js +80 -0
- package/dist/commands/validate.d.ts +6 -0
- package/dist/commands/validate.js +72 -0
- package/dist/config.js +1 -1
- package/dist/convex.d.ts +34 -22
- package/dist/convex.js +38 -22
- package/dist/harness/cache.d.ts +1 -5
- package/dist/harness/cache.js +49 -59
- package/dist/harness/convexReporting.d.ts +1 -1
- package/dist/harness/convexReporting.js +9 -7
- package/dist/harness/coverageCache.js +3 -3
- package/dist/harness/finalize.js +59 -46
- package/dist/harness/index.d.ts +6 -7
- package/dist/harness/index.js +9 -10
- package/dist/harness/prepare.js +6 -5
- package/dist/harness/requirementsLoader.d.ts +2 -2
- package/dist/harness/requirementsLoader.js +13 -35
- package/dist/harness/tracking.js +18 -18
- package/dist/harness/types.d.ts +1 -1
- package/dist/mcp/convexClient.d.ts +0 -39
- package/dist/mcp/convexClient.js +2 -107
- package/dist/mcp/grep.d.ts +1 -1
- package/dist/mcp/grep.js +87 -42
- package/dist/mcp/handlers/authoring.d.ts +1 -1
- package/dist/mcp/handlers/authoring.js +30 -234
- package/dist/mcp/handlers/coverage.d.ts +1 -1
- package/dist/mcp/handlers/coverage.js +13 -15
- package/dist/mcp/handlers/debug.d.ts +2 -3
- package/dist/mcp/handlers/debug.js +10 -10
- package/dist/mcp/handlers/get.d.ts +1 -1
- package/dist/mcp/handlers/get.js +11 -10
- package/dist/mcp/handlers/index.d.ts +20 -20
- package/dist/mcp/handlers/index.js +10 -10
- package/dist/mcp/handlers/list.d.ts +4 -33
- package/dist/mcp/handlers/list.js +16 -38
- package/dist/mcp/handlers/push.d.ts +1 -1
- package/dist/mcp/handlers/push.js +28 -18
- package/dist/mcp/handlers/report.d.ts +16 -0
- package/dist/mcp/handlers/report.js +134 -0
- package/dist/mcp/handlers/review.d.ts +1 -1
- package/dist/mcp/handlers/review.js +40 -59
- package/dist/mcp/handlers/search.d.ts +1 -1
- package/dist/mcp/handlers/search.js +7 -9
- package/dist/mcp/handlers/test-mapping.d.ts +1 -1
- package/dist/mcp/handlers/test-mapping.js +14 -14
- package/dist/mcp/handlers/types.d.ts +3 -3
- package/dist/mcp/handlers/types.js +2 -2
- package/dist/mcp/index.d.ts +1 -1
- package/dist/mcp/index.js +156 -178
- package/dist/mcp/requirements.d.ts +2 -2
- package/dist/mcp/requirements.js +30 -30
- package/dist/mcp/testCodeExtractor.js +24 -26
- package/dist/mcp/types.d.ts +1 -1
- package/dist/push/core.d.ts +2 -2
- package/dist/push/core.js +20 -20
- package/dist/push/index.d.ts +1 -1
- package/dist/push/index.js +2 -2
- package/dist/requirements/cloud-ai.d.ts +57 -0
- package/dist/requirements/cloud-ai.js +104 -0
- package/dist/requirements/cloud-coverage.d.ts +41 -0
- package/dist/requirements/cloud-coverage.js +60 -0
- package/dist/requirements/coverage.d.ts +45 -0
- package/dist/requirements/coverage.js +114 -0
- package/dist/requirements/grep.d.ts +33 -0
- package/dist/requirements/grep.js +306 -0
- package/dist/requirements/index.d.ts +73 -0
- package/dist/requirements/index.js +174 -0
- package/dist/requirements/style-guide.d.ts +67 -0
- package/dist/requirements/style-guide.js +299 -0
- package/dist/requirements/testCodeExtractor.d.ts +22 -0
- package/dist/requirements/testCodeExtractor.js +150 -0
- package/dist/schema/browser.d.ts +8 -6
- package/dist/schema/browser.js +14 -14
- package/dist/schema/builder.d.ts +1 -1
- package/dist/schema/builder.js +13 -44
- package/dist/schema/conversions.d.ts +2 -2
- package/dist/schema/conversions.js +11 -11
- package/dist/schema/index.d.ts +9 -7
- package/dist/schema/index.js +15 -13
- package/dist/schema/parser-core.d.ts +1 -1
- package/dist/schema/parser-core.js +23 -22
- package/dist/schema/parser.d.ts +3 -3
- package/dist/schema/parser.js +27 -31
- package/dist/schema/resolver.d.ts +1 -1
- package/dist/schema/resolver.js +9 -9
- package/dist/schema/scenario.d.ts +91 -0
- package/dist/schema/scenario.js +82 -0
- package/dist/schema/schemas.d.ts +3 -3
- package/dist/schema/schemas.js +41 -28
- package/dist/schema/test-schema.js +27 -27
- package/dist/templates/context-file-section.md +3 -2
- package/dist/templates/example-requirements.js +1 -1
- package/dist/templates/example-requirements.ts +3 -1
- package/dist/templates/requirements-readme.js +1 -1
- package/dist/templates/requirements-readme.ts +1 -1
- package/dist/templates/skills/codebase-to-spec/SKILL.md +118 -0
- package/dist/utils/brand.js +3 -3
- package/dist/utils/browser-launch.js +4 -4
- package/dist/utils/context-file.d.ts +1 -1
- package/dist/utils/context-file.js +26 -26
- package/dist/utils/env.js +7 -7
- package/dist/utils/gitignore.js +7 -7
- package/dist/utils/oauth-callback-server.d.ts +1 -1
- package/dist/utils/oauth-callback-server.js +27 -25
- package/dist/utils/oauth-flow.js +32 -29
- package/dist/utils/project-discovery.d.ts +3 -3
- package/dist/utils/project-discovery.js +18 -17
- package/dist/utils/project-name.js +8 -8
- package/dist/utils/project-selector.d.ts +1 -1
- package/dist/utils/project-selector.js +24 -21
- package/dist/utils/project-settings.d.ts +5 -4
- package/dist/utils/project-settings.js +28 -20
- package/dist/utils/templates.js +6 -6
- package/package.json +2 -1
|
@@ -1,65 +1,43 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
*/
|
|
6
|
-
import { textResponse } from './types.js';
|
|
7
|
-
import { getReferencedRequirementIds } from '../grep.js';
|
|
8
|
-
/**
|
|
9
|
-
* Handler for list_all_requirements tool
|
|
10
|
-
*
|
|
11
|
-
* Requirements covered:
|
|
12
|
-
* - MCP-LIST-1.0: Response includes count of total requirements and total nodes
|
|
13
|
-
* - MCP-LIST-1.1: Each requirement displays its key, label, and child count
|
|
14
|
-
* - MCP-LIST-1.2: Requirements are listed from all files discovered in the project
|
|
15
|
-
*/
|
|
16
|
-
export async function handleListAllRequirements(args, context) {
|
|
17
|
-
const { projectId } = args;
|
|
1
|
+
import { getReferencedRequirementIds } from "../../requirements/grep.js";
|
|
2
|
+
import { textResponse } from "./types.js";
|
|
3
|
+
export async function handleList(args, context) {
|
|
4
|
+
const { untested = false, projectId } = args;
|
|
18
5
|
const project = await context.getProjectFromDiscovery(projectId);
|
|
19
6
|
const requirements = await context.getRequirements(projectId);
|
|
20
7
|
if (requirements.length === 0) {
|
|
21
8
|
return textResponse(`No requirements found in project: ${project.path}\n\nMake sure .requirements/ directory exists with Markdown files (*.requirements.md).`);
|
|
22
9
|
}
|
|
23
|
-
|
|
10
|
+
if (untested) {
|
|
11
|
+
return formatUntested(requirements, project.path);
|
|
12
|
+
}
|
|
13
|
+
return formatAll(requirements);
|
|
14
|
+
}
|
|
15
|
+
function formatAll(requirements) {
|
|
24
16
|
const grouped = new Map();
|
|
25
17
|
for (const req of requirements) {
|
|
26
18
|
const existing = grouped.get(req.rootId) || [];
|
|
27
19
|
existing.push(req);
|
|
28
20
|
grouped.set(req.rootId, existing);
|
|
29
21
|
}
|
|
30
|
-
// MCP-LIST-1.1: Each requirement displays key, label (from content), and child count
|
|
31
22
|
const formatted = Array.from(grouped.entries())
|
|
32
23
|
.map(([rootId, reqs]) => {
|
|
33
24
|
const root = reqs.find((r) => r.path.length === 0);
|
|
34
25
|
const childCount = reqs.length - 1;
|
|
35
|
-
return `- **${rootId}**: ${root?.content ||
|
|
26
|
+
return `- **${rootId}**: ${root?.content || "(no content)"} (${childCount} children)`;
|
|
36
27
|
})
|
|
37
|
-
.join(
|
|
38
|
-
// MCP-LIST-1.0: Response includes count of total requirements and total nodes
|
|
28
|
+
.join("\n");
|
|
39
29
|
return textResponse(`Found ${grouped.size} requirement(s) with ${requirements.length} total nodes:\n\n${formatted}`);
|
|
40
30
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
*
|
|
44
|
-
* Requirements covered:
|
|
45
|
-
* - MCP-LIST-2.0: Only requirements with no test references are included
|
|
46
|
-
* - MCP-LIST-2.1: Response indicates count of requirements lacking coverage
|
|
47
|
-
*/
|
|
48
|
-
export async function handleListUntestedRequirements(args, context) {
|
|
49
|
-
const { projectId } = args;
|
|
50
|
-
const project = await context.getProjectFromDiscovery(projectId);
|
|
51
|
-
const requirements = await context.getRequirements(projectId);
|
|
52
|
-
const referencedIds = await getReferencedRequirementIds(project.path);
|
|
53
|
-
// MCP-LIST-2.0: Filter to root requirements without test references
|
|
31
|
+
async function formatUntested(requirements, projectPath) {
|
|
32
|
+
const referencedIds = await getReferencedRequirementIds(projectPath);
|
|
54
33
|
const rootRequirements = requirements.filter((r) => r.path.length === 0);
|
|
55
34
|
const untested = rootRequirements.filter((r) => !referencedIds.has(r.id));
|
|
56
35
|
if (untested.length === 0) {
|
|
57
|
-
return textResponse(`All ${rootRequirements.length}
|
|
36
|
+
return textResponse(`All ${rootRequirements.length} requirement(s) have test references — coverage is complete.`);
|
|
58
37
|
}
|
|
59
38
|
const formatted = untested
|
|
60
39
|
.map((r) => `- **${r.id}**: ${r.content}`)
|
|
61
|
-
.join(
|
|
62
|
-
// MCP-LIST-2.1: Response indicates count of requirements lacking coverage
|
|
40
|
+
.join("\n");
|
|
63
41
|
return textResponse(`Found ${untested.length} untested requirement(s) out of ${rootRequirements.length} total:\n\n${formatted}`);
|
|
64
42
|
}
|
|
65
43
|
//# sourceMappingURL=list.js.map
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Provides requirements push functionality:
|
|
5
5
|
* - push_requirements: Push local requirements to cloud
|
|
6
6
|
*/
|
|
7
|
-
import type { HandlerContext, ToolResponse } from
|
|
7
|
+
import type { HandlerContext, ToolResponse } from "./types.js";
|
|
8
8
|
/**
|
|
9
9
|
* Arguments for push_requirements tool
|
|
10
10
|
*/
|
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
* Provides requirements push functionality:
|
|
5
5
|
* - push_requirements: Push local requirements to cloud
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
7
|
+
import { existsSync } from "node:fs";
|
|
8
|
+
import { basename, resolve } from "node:path";
|
|
9
|
+
import { dryRunPush, executePush, parseFilesForPush, } from "../../push/index.js";
|
|
10
|
+
import { findRequirementsFiles } from "../../requirements/index.js";
|
|
11
|
+
import { CONVEX_URL } from "../convexClient.js";
|
|
12
|
+
import { errorResponse, textResponse } from "./types.js";
|
|
13
13
|
/**
|
|
14
14
|
* Handler for push_requirements tool
|
|
15
15
|
*
|
|
@@ -43,7 +43,7 @@ export async function handlePushRequirements(args, context) {
|
|
|
43
43
|
// Use same file discovery as CLI - searches entire workspace
|
|
44
44
|
filesToPush = await findRequirementsFiles(project.path);
|
|
45
45
|
if (filesToPush.length === 0) {
|
|
46
|
-
return textResponse(
|
|
46
|
+
return textResponse("No *.requirements.md files found. Nothing to push.");
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
// Parse files
|
|
@@ -64,9 +64,14 @@ export async function handlePushRequirements(args, context) {
|
|
|
64
64
|
// Only invalid files
|
|
65
65
|
const invalidList = dryRunResult.invalid
|
|
66
66
|
.map(({ file, result }) => `- ${basename(file.filePath)}: ${result.error}`)
|
|
67
|
-
.join(
|
|
67
|
+
.join("\n");
|
|
68
68
|
return {
|
|
69
|
-
content: [
|
|
69
|
+
content: [
|
|
70
|
+
{
|
|
71
|
+
type: "text",
|
|
72
|
+
text: `✗ No valid documents to push.\n\n**Invalid files:**\n${invalidList}`,
|
|
73
|
+
},
|
|
74
|
+
],
|
|
70
75
|
isError: true,
|
|
71
76
|
};
|
|
72
77
|
}
|
|
@@ -78,20 +83,20 @@ export async function handlePushRequirements(args, context) {
|
|
|
78
83
|
for (const { file, result } of dryRunResult.updates) {
|
|
79
84
|
const fileName = basename(file.filePath);
|
|
80
85
|
const hasConflict = dryRunResult.conflicts.some((c) => c.item.file === file);
|
|
81
|
-
const conflictNote = hasConflict ?
|
|
82
|
-
const warningNote = result.warning ? ` (${result.warning})` :
|
|
86
|
+
const conflictNote = hasConflict ? " ⚠️ [cloud changed since pull]" : "";
|
|
87
|
+
const warningNote = result.warning ? ` (${result.warning})` : "";
|
|
83
88
|
summary += `- ~ ${fileName}${conflictNote}${warningNote}\n`;
|
|
84
89
|
}
|
|
85
|
-
summary +=
|
|
90
|
+
summary += "\n";
|
|
86
91
|
}
|
|
87
92
|
if (dryRunResult.creates.length > 0) {
|
|
88
93
|
summary += `## New Documents (${dryRunResult.creates.length})\n`;
|
|
89
94
|
for (const { file, result } of dryRunResult.creates) {
|
|
90
95
|
const fileName = basename(file.filePath);
|
|
91
|
-
const warningNote = result.warning ? ` (${result.warning})` :
|
|
96
|
+
const warningNote = result.warning ? ` (${result.warning})` : "";
|
|
92
97
|
summary += `- + ${fileName}${warningNote}\n`;
|
|
93
98
|
}
|
|
94
|
-
summary +=
|
|
99
|
+
summary += "\n";
|
|
95
100
|
}
|
|
96
101
|
if (dryRunResult.notFound.length > 0) {
|
|
97
102
|
summary += `## Not Found in Cloud (${dryRunResult.notFound.length}) - will create new\n`;
|
|
@@ -99,7 +104,7 @@ export async function handlePushRequirements(args, context) {
|
|
|
99
104
|
const fileName = basename(file.filePath);
|
|
100
105
|
summary += `- ! ${fileName} (ID: ${result.documentId})\n`;
|
|
101
106
|
}
|
|
102
|
-
summary +=
|
|
107
|
+
summary += "\n";
|
|
103
108
|
}
|
|
104
109
|
if (dryRunResult.invalid.length > 0) {
|
|
105
110
|
summary += `## Skipped - Invalid (${dryRunResult.invalid.length})\n`;
|
|
@@ -107,7 +112,7 @@ export async function handlePushRequirements(args, context) {
|
|
|
107
112
|
const fileName = basename(file.filePath);
|
|
108
113
|
summary += `- ✗ ${fileName}: ${result.error}\n`;
|
|
109
114
|
}
|
|
110
|
-
summary +=
|
|
115
|
+
summary += "\n";
|
|
111
116
|
}
|
|
112
117
|
if (dryRunResult.conflicts.length > 0) {
|
|
113
118
|
summary += `⚠️ **Warning:** ${dryRunResult.conflicts.length} file(s) have cloud changes since last pull. Pushing will overwrite those changes.\n\n`;
|
|
@@ -118,7 +123,7 @@ export async function handlePushRequirements(args, context) {
|
|
|
118
123
|
// MCP-PUSH-1.1: When confirmed, execute push
|
|
119
124
|
try {
|
|
120
125
|
const result = await executePush(dryRunResult, credentials);
|
|
121
|
-
let output =
|
|
126
|
+
let output = "✓ Push complete!\n\n";
|
|
122
127
|
if (result.created > 0) {
|
|
123
128
|
output += `**Created:** ${result.created} document(s)\n`;
|
|
124
129
|
}
|
|
@@ -135,7 +140,12 @@ export async function handlePushRequirements(args, context) {
|
|
|
135
140
|
}
|
|
136
141
|
catch (error) {
|
|
137
142
|
return {
|
|
138
|
-
content: [
|
|
143
|
+
content: [
|
|
144
|
+
{
|
|
145
|
+
type: "text",
|
|
146
|
+
text: `✗ Push failed:\n\n${error instanceof Error ? error.message : String(error)}`,
|
|
147
|
+
},
|
|
148
|
+
],
|
|
139
149
|
isError: true,
|
|
140
150
|
};
|
|
141
151
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified coverage handler for the MCP `report_coverage` tool. Replaces
|
|
3
|
+
* `get_requirement_coverage` and `get_project_coverage_summary` with a
|
|
4
|
+
* single tool that takes a `source: "local" | "cloud"` argument and
|
|
5
|
+
* mirrors the CLI `dotreq report` semantics.
|
|
6
|
+
*/
|
|
7
|
+
import type { HandlerContext, ToolResponse } from "./types.js";
|
|
8
|
+
export interface ReportArgs {
|
|
9
|
+
source?: "local" | "cloud";
|
|
10
|
+
requirementKey?: string;
|
|
11
|
+
branch?: string;
|
|
12
|
+
sinceTimestamp?: number;
|
|
13
|
+
projectId?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function handleReport(args: ReportArgs, context: HandlerContext): Promise<ToolResponse>;
|
|
16
|
+
//# sourceMappingURL=report.d.ts.map
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified coverage handler for the MCP `report_coverage` tool. Replaces
|
|
3
|
+
* `get_requirement_coverage` and `get_project_coverage_summary` with a
|
|
4
|
+
* single tool that takes a `source: "local" | "cloud"` argument and
|
|
5
|
+
* mirrors the CLI `dotreq report` semantics.
|
|
6
|
+
*/
|
|
7
|
+
import { getProjectCoverage as queryProjectCoverage, getRequirementCoverage as queryRequirementCoverage, } from "../../requirements/cloud-coverage.js";
|
|
8
|
+
import { buildLocalReport, LocalReportCacheMissingError, } from "../../requirements/coverage.js";
|
|
9
|
+
import { CONVEX_URL } from "../convexClient.js";
|
|
10
|
+
import { errorResponse, textResponse } from "./types.js";
|
|
11
|
+
export async function handleReport(args, context) {
|
|
12
|
+
const source = args.source ?? "local";
|
|
13
|
+
if (source === "local") {
|
|
14
|
+
if (args.branch || args.sinceTimestamp !== undefined) {
|
|
15
|
+
return errorResponse("branch and sinceTimestamp require source=cloud. Local reports cover only the most recent test run on this machine.");
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
const report = buildLocalReport({
|
|
19
|
+
projectRoot: context.workspaceRoot,
|
|
20
|
+
requirementFilter: args.requirementKey,
|
|
21
|
+
});
|
|
22
|
+
return textResponse(formatLocalReport(report));
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
if (error instanceof LocalReportCacheMissingError) {
|
|
26
|
+
return errorResponse(error.message);
|
|
27
|
+
}
|
|
28
|
+
throw error;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
// source === "cloud"
|
|
32
|
+
let project;
|
|
33
|
+
try {
|
|
34
|
+
project = await context.getProjectFromDiscovery(args.projectId);
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
return errorResponse(`Cloud report requires cloud credentials. Run \`dotrequirements link\` to connect to cloud.\n\nError: ${error instanceof Error ? error.message : String(error)}`);
|
|
38
|
+
}
|
|
39
|
+
if (args.requirementKey) {
|
|
40
|
+
const record = await queryRequirementCoverage(args.requirementKey, project.projectId, project.projectSecret, CONVEX_URL);
|
|
41
|
+
return textResponse(formatCloudRequirement(record));
|
|
42
|
+
}
|
|
43
|
+
const record = await queryProjectCoverage(project.projectId, project.projectSecret, CONVEX_URL, {
|
|
44
|
+
branch: args.branch,
|
|
45
|
+
sinceTimestamp: args.sinceTimestamp,
|
|
46
|
+
});
|
|
47
|
+
return textResponse(formatCloudProject(record, {
|
|
48
|
+
branch: args.branch,
|
|
49
|
+
sinceTimestamp: args.sinceTimestamp,
|
|
50
|
+
}));
|
|
51
|
+
}
|
|
52
|
+
function formatLocalReport(report) {
|
|
53
|
+
const { total, tested, untested, coveragePercent } = report.summary;
|
|
54
|
+
let out = `# Coverage Report (local)\n\n`;
|
|
55
|
+
if (report.warnings.length > 0) {
|
|
56
|
+
for (const w of report.warnings) {
|
|
57
|
+
out += `> ⚠️ ${w}\n`;
|
|
58
|
+
}
|
|
59
|
+
out += "\n";
|
|
60
|
+
}
|
|
61
|
+
out += `**Total:** ${total} | **Tested:** ${tested} | **Untested:** ${untested} | **Coverage:** ${coveragePercent}%\n\n`;
|
|
62
|
+
const testedEntries = report.entries.filter((c) => c.tested);
|
|
63
|
+
if (testedEntries.length > 0) {
|
|
64
|
+
out += `## Tested (${testedEntries.length})\n\n`;
|
|
65
|
+
for (const c of testedEntries) {
|
|
66
|
+
out += `- **${c.key}**: ${c.content}\n`;
|
|
67
|
+
for (const loc of c.locations) {
|
|
68
|
+
out += ` - \`${loc}\`\n`;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
out += "\n";
|
|
72
|
+
}
|
|
73
|
+
const untestedEntries = report.entries.filter((c) => !c.tested);
|
|
74
|
+
if (untestedEntries.length > 0) {
|
|
75
|
+
out += `## Untested (${untestedEntries.length})\n\n`;
|
|
76
|
+
for (const c of untestedEntries) {
|
|
77
|
+
out += `- **${c.key}**: ${c.content}\n`;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return out;
|
|
81
|
+
}
|
|
82
|
+
function formatCloudRequirement(record) {
|
|
83
|
+
if (!record.lastTestedAt) {
|
|
84
|
+
return `**${record.requirementKey}** has no cloud coverage records.`;
|
|
85
|
+
}
|
|
86
|
+
const lastTested = new Date(record.lastTestedAt).toISOString();
|
|
87
|
+
let out = `# Coverage for ${record.requirementKey} (cloud)\n\n`;
|
|
88
|
+
out += `**Last tested:** ${lastTested}\n`;
|
|
89
|
+
if (record.branch)
|
|
90
|
+
out += `**Branch:** ${record.branch}\n`;
|
|
91
|
+
if (record.testFile) {
|
|
92
|
+
out += `**Test location:** \`${record.testFile}${record.testLine ? `:${record.testLine}` : ""}\`\n`;
|
|
93
|
+
}
|
|
94
|
+
const otherBranches = record.allBranches.filter((b) => b.branch !== record.branch);
|
|
95
|
+
if (otherBranches.length > 0) {
|
|
96
|
+
out += `\n**Also tested on:**\n`;
|
|
97
|
+
for (const b of otherBranches) {
|
|
98
|
+
out += `- ${b.branch}: ${new Date(b.lastTestedAt).toISOString()}\n`;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return out;
|
|
102
|
+
}
|
|
103
|
+
function formatCloudProject(record, filters) {
|
|
104
|
+
const tested = record.tested.length;
|
|
105
|
+
const untested = record.untested.length;
|
|
106
|
+
const total = tested + untested;
|
|
107
|
+
const coveragePercent = total > 0 ? ((tested / total) * 100).toFixed(1) : "0.0";
|
|
108
|
+
let filterNote = "";
|
|
109
|
+
if (filters.branch)
|
|
110
|
+
filterNote += `\n**Branch:** ${filters.branch}`;
|
|
111
|
+
if (filters.sinceTimestamp) {
|
|
112
|
+
filterNote += `\n**Since:** ${new Date(filters.sinceTimestamp).toISOString()}`;
|
|
113
|
+
}
|
|
114
|
+
let out = `# Project Coverage Report (cloud)\n\n`;
|
|
115
|
+
out += `**Total Requirements:** ${total}\n**Coverage:** ${coveragePercent}%${filterNote}\n`;
|
|
116
|
+
if (record.tested.length > 0) {
|
|
117
|
+
out += `\n## Tested Requirements (${record.tested.length})\n\n`;
|
|
118
|
+
for (const t of record.tested) {
|
|
119
|
+
const date = new Date(t.lastTestedAt).toISOString();
|
|
120
|
+
const location = t.testFile
|
|
121
|
+
? ` (\`${t.testFile}${t.testLine ? `:${t.testLine}` : ""}\`)`
|
|
122
|
+
: "";
|
|
123
|
+
out += `- **${t.requirementKey}** [${t.branch}]: ${date}${location}\n`;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (record.untested.length > 0) {
|
|
127
|
+
out += `\n## Untested Requirements (${record.untested.length})\n\n`;
|
|
128
|
+
for (const key of record.untested) {
|
|
129
|
+
out += `- ${key}\n`;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return out;
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=report.js.map
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* - style_check: Check requirements or test files for style issues
|
|
6
6
|
* - review_test: Comprehensively review tests for semantic correctness
|
|
7
7
|
*/
|
|
8
|
-
import type { HandlerContext, ToolResponse } from
|
|
8
|
+
import type { HandlerContext, ToolResponse } from "./types.js";
|
|
9
9
|
/**
|
|
10
10
|
* Arguments for style_check tool
|
|
11
11
|
*/
|
|
@@ -5,11 +5,12 @@
|
|
|
5
5
|
* - style_check: Check requirements or test files for style issues
|
|
6
6
|
* - review_test: Comprehensively review tests for semantic correctness
|
|
7
7
|
*/
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import { filterRequirementsByKeys,
|
|
12
|
-
import { discoverProjects, } from
|
|
8
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
9
|
+
import { dirname, resolve } from "node:path";
|
|
10
|
+
import { fetchReviewTestFeedback, fetchStyleCheckFeedback, } from "../../requirements/cloud-ai.js";
|
|
11
|
+
import { filterRequirementsByKeys, formatRequirementTree, getRequirementTree, } from "../../requirements/index.js";
|
|
12
|
+
import { discoverProjects, } from "../../utils/project-discovery.js";
|
|
13
|
+
import { errorResponse, textResponse } from "./types.js";
|
|
13
14
|
const STYLE_CHECK_GUIDANCE = `
|
|
14
15
|
|
|
15
16
|
---
|
|
@@ -32,24 +33,24 @@ const STYLE_CHECK_GUIDANCE = `
|
|
|
32
33
|
*/
|
|
33
34
|
export async function handleStyleCheck(args, context, options) {
|
|
34
35
|
const { filePath, requirementKeys, model } = args;
|
|
35
|
-
const { useEnvAuth = false, apiBaseUrl =
|
|
36
|
+
const { useEnvAuth = false, apiBaseUrl = "https://app.dotrequirements.io" } = options ?? {};
|
|
36
37
|
const fullPath = resolve(context.workspaceRoot, filePath);
|
|
37
38
|
if (!existsSync(fullPath)) {
|
|
38
39
|
return errorResponse(`File not found: ${filePath}`);
|
|
39
40
|
}
|
|
40
41
|
// Read file contents
|
|
41
|
-
const fileContents = readFileSync(fullPath,
|
|
42
|
+
const fileContents = readFileSync(fullPath, "utf-8");
|
|
42
43
|
// Detect file type
|
|
43
44
|
// MCP-REVIEW-1.0 & MCP-REVIEW-1.1: Different feedback for different file types
|
|
44
|
-
const isRequirementsFile = filePath.endsWith(
|
|
45
|
+
const isRequirementsFile = filePath.endsWith(".requirements.md");
|
|
45
46
|
const isTestFile = /\.(test|spec)\.(js|jsx|ts|tsx)$/.test(filePath);
|
|
46
47
|
if (!isRequirementsFile && !isTestFile) {
|
|
47
48
|
return errorResponse(`Unsupported file type. File must be:\n- Requirements file: *.requirements.md\n- Test file: *.test.{js,jsx,ts,tsx} or *.spec.{js,jsx,ts,tsx}`);
|
|
48
49
|
}
|
|
49
|
-
const fileType = isRequirementsFile ?
|
|
50
|
+
const fileType = isRequirementsFile ? "requirements" : "test";
|
|
50
51
|
// If requirementKeys provided, filter the file to only those requirements
|
|
51
52
|
let fileContentsToCheck = fileContents;
|
|
52
|
-
let scopeNote =
|
|
53
|
+
let scopeNote = "";
|
|
53
54
|
if (requirementKeys && requirementKeys.length > 0) {
|
|
54
55
|
if (!isRequirementsFile) {
|
|
55
56
|
return errorResponse(`requirementKeys can only be used with requirements files (*.requirements.md), not test files.`);
|
|
@@ -57,11 +58,11 @@ export async function handleStyleCheck(args, context, options) {
|
|
|
57
58
|
try {
|
|
58
59
|
const { filteredContent, foundKeys, missingKeys } = filterRequirementsByKeys(fileContents, requirementKeys);
|
|
59
60
|
if (foundKeys.length === 0) {
|
|
60
|
-
return errorResponse(`None of the specified requirement keys were found in ${filePath}: ${requirementKeys.join(
|
|
61
|
+
return errorResponse(`None of the specified requirement keys were found in ${filePath}: ${requirementKeys.join(", ")}`);
|
|
61
62
|
}
|
|
62
63
|
fileContentsToCheck = filteredContent;
|
|
63
64
|
if (missingKeys.length > 0) {
|
|
64
|
-
scopeNote = `\n\n> **Note:** Some specified keys were not found in the file: ${missingKeys.join(
|
|
65
|
+
scopeNote = `\n\n> **Note:** Some specified keys were not found in the file: ${missingKeys.join(", ")}`;
|
|
65
66
|
}
|
|
66
67
|
}
|
|
67
68
|
catch (error) {
|
|
@@ -79,50 +80,40 @@ export async function handleStyleCheck(args, context, options) {
|
|
|
79
80
|
else {
|
|
80
81
|
// Try to find a project starting from the file's directory
|
|
81
82
|
const result = await discoverProjects(fileDir);
|
|
82
|
-
if (result.type ===
|
|
83
|
+
if (result.type === "none") {
|
|
83
84
|
// If no project found from file dir, try from context
|
|
84
85
|
project = await context.getProjectFromDiscovery();
|
|
85
86
|
}
|
|
86
|
-
else if (result.type ===
|
|
87
|
+
else if (result.type === "single") {
|
|
87
88
|
project = result.project;
|
|
88
89
|
}
|
|
89
90
|
else {
|
|
90
91
|
// Multiple projects - can't auto-detect which one to use
|
|
91
|
-
throw new Error(
|
|
92
|
+
throw new Error("Multiple projects found - cannot auto-detect for this file");
|
|
92
93
|
}
|
|
93
94
|
}
|
|
94
95
|
}
|
|
95
96
|
catch (error) {
|
|
96
97
|
return errorResponse(`Style check requires project credentials. Run \`dotrequirements link\` to connect to cloud.\n\nError: ${error instanceof Error ? error.message : String(error)}`);
|
|
97
98
|
}
|
|
98
|
-
// Call
|
|
99
|
+
// Call hosted endpoint via the shared library helper
|
|
99
100
|
try {
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
projectSecret: project.projectSecret,
|
|
108
|
-
fileContents: fileContentsToCheck,
|
|
109
|
-
fileType,
|
|
110
|
-
model,
|
|
111
|
-
}),
|
|
101
|
+
const feedback = await fetchStyleCheckFeedback({
|
|
102
|
+
apiBaseUrl,
|
|
103
|
+
projectId: project.projectId,
|
|
104
|
+
projectSecret: project.projectSecret,
|
|
105
|
+
fileContents: fileContentsToCheck,
|
|
106
|
+
fileType,
|
|
107
|
+
model,
|
|
112
108
|
});
|
|
113
|
-
if (!response.ok) {
|
|
114
|
-
const errorData = await response.json();
|
|
115
|
-
return errorResponse(`Style check failed: ${errorData.error || response.statusText}`);
|
|
116
|
-
}
|
|
117
|
-
const data = await response.json();
|
|
118
109
|
const scopeLabel = requirementKeys && requirementKeys.length > 0
|
|
119
|
-
? ` (${requirementKeys.join(
|
|
120
|
-
:
|
|
110
|
+
? ` (${requirementKeys.join(", ")})`
|
|
111
|
+
: "";
|
|
121
112
|
// MCP-REVIEW-1.2: Feedback includes severity categories (in the API response)
|
|
122
|
-
return textResponse(`# Style Check Results for \`${filePath}\`${scopeLabel}\n\n${
|
|
113
|
+
return textResponse(`# Style Check Results for \`${filePath}\`${scopeLabel}\n\n${feedback}${scopeNote}${STYLE_CHECK_GUIDANCE}`);
|
|
123
114
|
}
|
|
124
115
|
catch (error) {
|
|
125
|
-
return errorResponse(`Style check
|
|
116
|
+
return errorResponse(`Style check failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
126
117
|
}
|
|
127
118
|
}
|
|
128
119
|
/**
|
|
@@ -138,7 +129,7 @@ export async function handleStyleCheck(args, context, options) {
|
|
|
138
129
|
*/
|
|
139
130
|
export async function handleReviewTest(args, context, options) {
|
|
140
131
|
const { testFilePath, projectId } = args;
|
|
141
|
-
const { apiBaseUrl =
|
|
132
|
+
const { apiBaseUrl = "https://app.dotrequirements.io" } = options ?? {};
|
|
142
133
|
const fullPath = resolve(context.workspaceRoot, testFilePath);
|
|
143
134
|
if (!existsSync(fullPath)) {
|
|
144
135
|
return errorResponse(`File not found: ${testFilePath}`);
|
|
@@ -149,7 +140,7 @@ export async function handleReviewTest(args, context, options) {
|
|
|
149
140
|
return errorResponse(`File must be a test file: *.test.{js,jsx,ts,tsx} or *.spec.{js,jsx,ts,tsx}`);
|
|
150
141
|
}
|
|
151
142
|
// Read test file contents
|
|
152
|
-
const testFileContents = readFileSync(fullPath,
|
|
143
|
+
const testFileContents = readFileSync(fullPath, "utf-8");
|
|
153
144
|
// Extract requirement IDs from the test file using regex
|
|
154
145
|
const requirementIdMatches = testFileContents.matchAll(/requirement\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g);
|
|
155
146
|
const requirementIds = new Set();
|
|
@@ -170,7 +161,7 @@ export async function handleReviewTest(args, context, options) {
|
|
|
170
161
|
// e.g., NAV-3.0, NAV-3.1, NAV-3.2 all belong to root NAV-3
|
|
171
162
|
const rootToTestedIds = new Map();
|
|
172
163
|
for (const reqId of requirementIds) {
|
|
173
|
-
const req = allRequirements.find(r => r.id === reqId || r.rootId === reqId);
|
|
164
|
+
const req = allRequirements.find((r) => r.id === reqId || r.rootId === reqId);
|
|
174
165
|
if (req) {
|
|
175
166
|
if (!rootToTestedIds.has(req.rootId)) {
|
|
176
167
|
rootToTestedIds.set(req.rootId, new Set());
|
|
@@ -190,30 +181,20 @@ export async function handleReviewTest(args, context, options) {
|
|
|
190
181
|
testedIds: Array.from(testedIds).sort(),
|
|
191
182
|
});
|
|
192
183
|
}
|
|
193
|
-
// Call
|
|
184
|
+
// Call hosted endpoint via the shared library helper
|
|
194
185
|
// MCP-REVIEW-2.3 & 2.4: API identifies coverage gaps and non-existent references
|
|
195
186
|
try {
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
projectId: project.projectId,
|
|
203
|
-
projectSecret: project.projectSecret,
|
|
204
|
-
testFileContents,
|
|
205
|
-
requirements,
|
|
206
|
-
}),
|
|
187
|
+
const feedback = await fetchReviewTestFeedback({
|
|
188
|
+
apiBaseUrl,
|
|
189
|
+
projectId: project.projectId,
|
|
190
|
+
projectSecret: project.projectSecret,
|
|
191
|
+
testFileContents,
|
|
192
|
+
requirements,
|
|
207
193
|
});
|
|
208
|
-
|
|
209
|
-
const errorData = await response.json();
|
|
210
|
-
return errorResponse(`Test review failed: ${errorData.error || response.statusText}`);
|
|
211
|
-
}
|
|
212
|
-
const data = await response.json();
|
|
213
|
-
return textResponse(`# Test Review Results for \`${testFilePath}\`\n\n${data.feedback}`);
|
|
194
|
+
return textResponse(`# Test Review Results for \`${testFilePath}\`\n\n${feedback}`);
|
|
214
195
|
}
|
|
215
196
|
catch (error) {
|
|
216
|
-
return errorResponse(`Test review
|
|
197
|
+
return errorResponse(`Test review failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
217
198
|
}
|
|
218
199
|
}
|
|
219
200
|
//# sourceMappingURL=review.js.map
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Searches requirements by text or regex query across IDs, content, and labels.
|
|
5
5
|
* Returns matching requirements with their full tree context.
|
|
6
6
|
*/
|
|
7
|
-
import type { HandlerContext, ToolResponse } from
|
|
7
|
+
import type { HandlerContext, ToolResponse } from "./types.js";
|
|
8
8
|
/**
|
|
9
9
|
* Arguments for search_requirements tool
|
|
10
10
|
*/
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* Searches requirements by text or regex query across IDs, content, and labels.
|
|
5
5
|
* Returns matching requirements with their full tree context.
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
7
|
+
import { formatRequirementTree, getRequirementTree, searchRequirements, } from "../../requirements/index.js";
|
|
8
|
+
import { errorResponse, textResponse } from "./types.js";
|
|
9
9
|
/**
|
|
10
10
|
* Handler for search_requirements tool
|
|
11
11
|
*
|
|
@@ -27,10 +27,8 @@ export async function handleSearchRequirements(args, context) {
|
|
|
27
27
|
// MCP-SEARCH-1.3: Interpret query as case-insensitive regex
|
|
28
28
|
// MCP-SEARCH-1.4: Return error for invalid regex
|
|
29
29
|
try {
|
|
30
|
-
const regex = new RegExp(query,
|
|
31
|
-
results = requirements.filter((r) => regex.test(r.id) ||
|
|
32
|
-
regex.test(r.content) ||
|
|
33
|
-
regex.test(r.label));
|
|
30
|
+
const regex = new RegExp(query, "i");
|
|
31
|
+
results = requirements.filter((r) => regex.test(r.id) || regex.test(r.content) || regex.test(r.label));
|
|
34
32
|
}
|
|
35
33
|
catch (error) {
|
|
36
34
|
return errorResponse(`Invalid regular expression: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -45,7 +43,7 @@ export async function handleSearchRequirements(args, context) {
|
|
|
45
43
|
const rootResults = requirements.filter((r) => r.path.length === 0 && matchedRootIds.has(r.rootId));
|
|
46
44
|
// MCP-SEARCH-1.5: No matches returns informative message
|
|
47
45
|
if (rootResults.length === 0) {
|
|
48
|
-
return textResponse(`No requirements found matching "${query}"${useRegex ?
|
|
46
|
+
return textResponse(`No requirements found matching "${query}"${useRegex ? " (regex)" : ""}`);
|
|
49
47
|
}
|
|
50
48
|
// MCP-SEARCH-1.7: Each result includes full tree as code block
|
|
51
49
|
const formatted = rootResults
|
|
@@ -54,7 +52,7 @@ export async function handleSearchRequirements(args, context) {
|
|
|
54
52
|
const treeFormatted = formatRequirementTree(tree);
|
|
55
53
|
return `**${r.id}** (${r.label})\n${r.content}\n_Source: ${r.documentTitle}_\n\n\`\`\`\n${treeFormatted}\n\`\`\``;
|
|
56
54
|
})
|
|
57
|
-
.join(
|
|
58
|
-
return textResponse(`Found ${rootResults.length} requirement(s) matching "${query}"${useRegex ?
|
|
55
|
+
.join("\n\n---\n\n");
|
|
56
|
+
return textResponse(`Found ${rootResults.length} requirement(s) matching "${query}"${useRegex ? " (regex)" : ""}:\n\n${formatted}`);
|
|
59
57
|
}
|
|
60
58
|
//# sourceMappingURL=search.js.map
|