@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
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Authoring handlers for MCP tools
|
|
3
|
-
*
|
|
4
|
-
* Provides document creation and validation:
|
|
5
|
-
* - create_requirement_document: Generate requirements template with format guidance
|
|
6
|
-
* - validate_requirements: Validate requirements file syntax offline
|
|
7
|
-
*/
|
|
8
|
-
import { existsSync } from "node:fs";
|
|
9
|
-
import { getProjectContext } from "../../requirements/cloud-ai.js";
|
|
10
|
-
import { generateStyleGuide, readLocalStyleGuide, } from "../../requirements/style-guide.js";
|
|
11
|
-
import { parseRequirementsFromFile, validateForPush, } from "../../schema/index.js";
|
|
12
|
-
import { CONVEX_URL } from "../convexClient.js";
|
|
13
|
-
import { errorResponse, resolveProjectFilePath, textResponse, } from "./types.js";
|
|
14
|
-
/**
|
|
15
|
-
* Handler for create_requirement_document tool
|
|
16
|
-
*
|
|
17
|
-
* Requirements covered:
|
|
18
|
-
* - MCP-AUTHOR-1.0: The template includes format guidance with code block examples
|
|
19
|
-
* - MCP-AUTHOR-1.1: The template includes style guidance (concrete examples, concise prose, testable conditions)
|
|
20
|
-
* - MCP-AUTHOR-1.2: When the project has requirementsStyleContext configured, it is included in the template
|
|
21
|
-
* - MCP-AUTHOR-1.3: When cloud credentials are unavailable, the template works without the custom context
|
|
22
|
-
*/
|
|
23
|
-
export async function handleCreateRequirementDocument(args, context) {
|
|
24
|
-
const { filePath = ".requirements/example.requirements.md" } = args;
|
|
25
|
-
// MCP-AUTHOR-1.3: Try to get requirements, but don't fail if no project is configured
|
|
26
|
-
let requirements = [];
|
|
27
|
-
try {
|
|
28
|
-
requirements = await context.getRequirements();
|
|
29
|
-
}
|
|
30
|
-
catch {
|
|
31
|
-
// No project configured — discovered patterns will be empty
|
|
32
|
-
}
|
|
33
|
-
// MCP-AUTHOR-1.2: Try to fetch user-provided style context from cloud
|
|
34
|
-
let customStyleGuidance = null;
|
|
35
|
-
try {
|
|
36
|
-
const project = await context.getProjectFromDiscovery();
|
|
37
|
-
const contextData = await getProjectContext(project.projectId, project.projectSecret, CONVEX_URL);
|
|
38
|
-
customStyleGuidance = contextData?.requirementsStyleContext ?? null;
|
|
39
|
-
}
|
|
40
|
-
catch {
|
|
41
|
-
// MCP-AUTHOR-1.3: No credentials or cloud unavailable - continue without user context
|
|
42
|
-
}
|
|
43
|
-
const localStyleGuide = readLocalStyleGuide(context.workspaceRoot);
|
|
44
|
-
return textResponse(generateStyleGuide({
|
|
45
|
-
requirements,
|
|
46
|
-
customStyleGuidance,
|
|
47
|
-
filePath,
|
|
48
|
-
localStyleGuide,
|
|
49
|
-
}));
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Handler for validate_requirements tool
|
|
53
|
-
*
|
|
54
|
-
* Requirements covered:
|
|
55
|
-
* - MCP-AUTHOR-2.1: When the file has valid syntax, the response confirms validation passed
|
|
56
|
-
* - MCP-AUTHOR-2.2: When the file has syntax errors, the response lists each error with location
|
|
57
|
-
* - MCP-AUTHOR-2.3: Validation does not require network access or cloud credentials
|
|
58
|
-
* - MCP-AUTHOR-2.4: When the file does not exist, an error is returned
|
|
59
|
-
*/
|
|
60
|
-
export async function handleValidateRequirements(args, context) {
|
|
61
|
-
const { filePath } = args;
|
|
62
|
-
// #49: Resolve against the discovered project.path (matching push) with a
|
|
63
|
-
// workspaceRoot fallback. MCP-AUTHOR-2.3: validation stays offline — if
|
|
64
|
-
// discovery has no credentials it simply throws and we fall back to
|
|
65
|
-
// workspaceRoot resolution.
|
|
66
|
-
let projectPath;
|
|
67
|
-
try {
|
|
68
|
-
projectPath = (await context.getProjectFromDiscovery()).path;
|
|
69
|
-
}
|
|
70
|
-
catch {
|
|
71
|
-
projectPath = undefined;
|
|
72
|
-
}
|
|
73
|
-
const fullPath = resolveProjectFilePath(filePath, projectPath, context.workspaceRoot);
|
|
74
|
-
// MCP-AUTHOR-2.4: Check if file exists
|
|
75
|
-
if (!existsSync(fullPath)) {
|
|
76
|
-
return errorResponse(`File not found: ${filePath}`);
|
|
77
|
-
}
|
|
78
|
-
try {
|
|
79
|
-
// MCP-AUTHOR-2.1 & MCP-AUTHOR-2.2: Parse and validate
|
|
80
|
-
const parsed = parseRequirementsFromFile(fullPath);
|
|
81
|
-
const reqCount = Object.keys(parsed.requirements).length;
|
|
82
|
-
// Check push readiness
|
|
83
|
-
const pushValidation = validateForPush(parsed.metadata);
|
|
84
|
-
let headline;
|
|
85
|
-
let pushDetails = "";
|
|
86
|
-
if (!pushValidation.valid) {
|
|
87
|
-
headline = `❌ \`${filePath}\` - Not push-ready\n- ${pushValidation.reason}`;
|
|
88
|
-
}
|
|
89
|
-
else {
|
|
90
|
-
const actionLabel = pushValidation.action === "create"
|
|
91
|
-
? "Will create new document"
|
|
92
|
-
: "Will update existing document";
|
|
93
|
-
headline = `✓ \`${filePath}\` - ${actionLabel}`;
|
|
94
|
-
if (pushValidation.warning) {
|
|
95
|
-
pushDetails = `\n- ⚠️ ${pushValidation.warning}`;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
return textResponse(`${headline}${pushDetails}\n\n**Metadata:**\n- Version: ${parsed.metadata.version ?? "(none)"}\n- Document: ${parsed.metadata.document?.title || "(none)"}\n- Document ID: ${parsed.metadata.document?.id || "(none)"}\n\n**Requirements:** ${reqCount} root requirement(s) found`);
|
|
99
|
-
}
|
|
100
|
-
catch (error) {
|
|
101
|
-
// MCP-AUTHOR-2.2: Syntax errors are returned with details
|
|
102
|
-
return {
|
|
103
|
-
content: [
|
|
104
|
-
{
|
|
105
|
-
type: "text",
|
|
106
|
-
text: `✗ Validation failed for \`${filePath}\`:\n\n${error instanceof Error ? error.message : String(error)}`,
|
|
107
|
-
},
|
|
108
|
-
],
|
|
109
|
-
isError: true,
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
//# sourceMappingURL=authoring.js.map
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* debug_mcp_environment handler
|
|
3
|
-
*
|
|
4
|
-
* Returns diagnostic information about the MCP server environment.
|
|
5
|
-
* Useful for debugging configuration issues.
|
|
6
|
-
*/
|
|
7
|
-
import type { HandlerContext, ToolResponse } from "./types.js";
|
|
8
|
-
/**
|
|
9
|
-
* Arguments for debug_mcp_environment tool
|
|
10
|
-
*/
|
|
11
|
-
export type DebugMcpEnvironmentArgs = Record<string, never>;
|
|
12
|
-
/**
|
|
13
|
-
* Handler for debug_mcp_environment tool
|
|
14
|
-
*/
|
|
15
|
-
export declare function handleDebugMcpEnvironment(_args: DebugMcpEnvironmentArgs, context: HandlerContext): Promise<ToolResponse>;
|
|
16
|
-
//# sourceMappingURL=debug.d.ts.map
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* debug_mcp_environment handler
|
|
3
|
-
*
|
|
4
|
-
* Returns diagnostic information about the MCP server environment.
|
|
5
|
-
* Useful for debugging configuration issues.
|
|
6
|
-
*/
|
|
7
|
-
import { textResponse } from "./types.js";
|
|
8
|
-
/**
|
|
9
|
-
* Handler for debug_mcp_environment tool
|
|
10
|
-
*/
|
|
11
|
-
export async function handleDebugMcpEnvironment(_args, context) {
|
|
12
|
-
// Check for Antigravity-specific env vars
|
|
13
|
-
const antigravityVars = Object.entries(context.env)
|
|
14
|
-
.filter(([key]) => key.startsWith("GEMINI_") || key.startsWith("ANTIGRAVITY_"))
|
|
15
|
-
.map(([key, value]) => ` ${key}: ${value}`)
|
|
16
|
-
.join("\n");
|
|
17
|
-
// Show discovered projects from env vars
|
|
18
|
-
const projectList = Array.from(context.projectPaths.entries())
|
|
19
|
-
.map(([id, path]) => ` ${id}: ${path}`)
|
|
20
|
-
.join("\n");
|
|
21
|
-
const text = `# MCP Server Environment Debug Info
|
|
22
|
-
|
|
23
|
-
**process.cwd():** ${process.cwd()}
|
|
24
|
-
**WORKSPACE_ROOT:** ${context.workspaceRoot}
|
|
25
|
-
**REQUIREMENTS_DIR env:** ${context.env.REQUIREMENTS_DIR || "(not set)"}
|
|
26
|
-
**HOME:** ${context.env.HOME || "(not set)"}
|
|
27
|
-
**PWD:** ${context.env.PWD || "(not set)"}
|
|
28
|
-
**OLDPWD:** ${context.env.OLDPWD || "(not set)"}
|
|
29
|
-
|
|
30
|
-
**Antigravity/Gemini env vars:**
|
|
31
|
-
${antigravityVars || "(none found)"}
|
|
32
|
-
|
|
33
|
-
**Projects from PROJ_* env vars:**
|
|
34
|
-
${projectList || "(none found)"}`;
|
|
35
|
-
return textResponse(text);
|
|
36
|
-
}
|
|
37
|
-
//# sourceMappingURL=debug.js.map
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* get_requirement handler
|
|
3
|
-
*
|
|
4
|
-
* Retrieves a specific requirement by ID with its full tree and test coverage.
|
|
5
|
-
*/
|
|
6
|
-
import type { HandlerContext, ToolResponse } from "./types.js";
|
|
7
|
-
/**
|
|
8
|
-
* Arguments for get_requirement tool
|
|
9
|
-
*/
|
|
10
|
-
export interface GetRequirementArgs {
|
|
11
|
-
id: string;
|
|
12
|
-
projectId?: string;
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Handler for get_requirement tool
|
|
16
|
-
*
|
|
17
|
-
* Requirements covered:
|
|
18
|
-
* - MCP-GET-1.0: When the ID matches a root requirement, the full tree is returned
|
|
19
|
-
* - MCP-GET-1.1: When the ID matches a child requirement, that subtree is returned
|
|
20
|
-
* - MCP-GET-1.2: When the ID does not exist, an error message is returned
|
|
21
|
-
* - MCP-GET-1.3: When a requirement is retrieved, test references are shown with file path and line number
|
|
22
|
-
*/
|
|
23
|
-
export declare function handleGetRequirement(args: GetRequirementArgs, context: HandlerContext): Promise<ToolResponse>;
|
|
24
|
-
//# sourceMappingURL=get.d.ts.map
|
package/dist/mcp/handlers/get.js
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* get_requirement handler
|
|
3
|
-
*
|
|
4
|
-
* Retrieves a specific requirement by ID with its full tree and test coverage.
|
|
5
|
-
*/
|
|
6
|
-
import { glob } from "glob";
|
|
7
|
-
import { formatRequirementTree } from "../../requirements/index.js";
|
|
8
|
-
import { findFilesWithRequirement, findTestCodeForRequirement, } from "../../requirements/testCodeExtractor.js";
|
|
9
|
-
import { textResponse } from "./types.js";
|
|
10
|
-
/**
|
|
11
|
-
* Handler for get_requirement tool
|
|
12
|
-
*
|
|
13
|
-
* Requirements covered:
|
|
14
|
-
* - MCP-GET-1.0: When the ID matches a root requirement, the full tree is returned
|
|
15
|
-
* - MCP-GET-1.1: When the ID matches a child requirement, that subtree is returned
|
|
16
|
-
* - MCP-GET-1.2: When the ID does not exist, an error message is returned
|
|
17
|
-
* - MCP-GET-1.3: When a requirement is retrieved, test references are shown with file path and line number
|
|
18
|
-
*/
|
|
19
|
-
export async function handleGetRequirement(args, context) {
|
|
20
|
-
const { id, projectId } = args;
|
|
21
|
-
const project = await context.getProjectFromDiscovery(projectId);
|
|
22
|
-
const requirements = await context.getRequirements(projectId);
|
|
23
|
-
// Get the tree starting from this ID.
|
|
24
|
-
// MCP-GET-1.0: Full tree for root requirement
|
|
25
|
-
// MCP-GET-1.1: Subtree for child requirement
|
|
26
|
-
//
|
|
27
|
-
// #46: Match the node itself plus all of its descendants by id prefix. The
|
|
28
|
-
// shared getRequirementTree only matches on rootId/id equality, which drops
|
|
29
|
-
// grandchildren (e.g. REQ-123.0.0) when a child id (REQ-123.0) is requested.
|
|
30
|
-
const tree = requirements.filter((r) => r.id === id || r.id.startsWith(`${id}.`));
|
|
31
|
-
// MCP-GET-1.2: Return error if not found
|
|
32
|
-
if (tree.length === 0) {
|
|
33
|
-
return textResponse(`Requirement "${id}" not found`);
|
|
34
|
-
}
|
|
35
|
-
const root = tree[0];
|
|
36
|
-
const formatted = formatRequirementTree(tree);
|
|
37
|
-
// MCP-GET-1.3: Find test files that reference this requirement
|
|
38
|
-
const testFiles = await glob("**/*.{test,spec}.{js,jsx,ts,tsx}", {
|
|
39
|
-
cwd: project.path,
|
|
40
|
-
absolute: true,
|
|
41
|
-
ignore: ["**/node_modules/**", "**/dist/**", "**/build/**"],
|
|
42
|
-
});
|
|
43
|
-
const matchingFiles = findFilesWithRequirement(testFiles, id);
|
|
44
|
-
const testCodeSections = [];
|
|
45
|
-
for (const file of matchingFiles) {
|
|
46
|
-
const testRefs = findTestCodeForRequirement(file, id);
|
|
47
|
-
// Deduplicate by removing nested blocks - keep only outermost blocks
|
|
48
|
-
const deduplicated = testRefs.filter((ref, i) => {
|
|
49
|
-
// Check if this ref is contained within any other ref
|
|
50
|
-
const isNested = testRefs.some((other, j) => {
|
|
51
|
-
if (i === j)
|
|
52
|
-
return false;
|
|
53
|
-
// other contains ref if it starts before or at the same line and ends after or at the same line
|
|
54
|
-
return (other.startLine <= ref.startLine &&
|
|
55
|
-
other.endLine >= ref.endLine &&
|
|
56
|
-
(other.startLine < ref.startLine || other.endLine > ref.endLine));
|
|
57
|
-
});
|
|
58
|
-
return !isNested;
|
|
59
|
-
});
|
|
60
|
-
for (const ref of deduplicated) {
|
|
61
|
-
testCodeSections.push(`**${ref.requirementId}** tested in \`${ref.file}:${ref.startLine}-${ref.endLine}\`:\n\n\`\`\`typescript\n${ref.code}\n\`\`\``);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
const testSection = testCodeSections.length > 0
|
|
65
|
-
? `\n\n## Test Coverage (${testCodeSections.length} reference(s)):\n\n${testCodeSections.join("\n\n---\n\n")}`
|
|
66
|
-
: "\n\n## Test Coverage\n\nNo tests found referencing this requirement.";
|
|
67
|
-
return textResponse(`# ${id}\n\n**Document:** ${root.documentTitle}\n**Source:** ${root.sourceFile}\n\n## Requirement Tree (${tree.length} node(s)):\n\n\`\`\`\n${formatted}\n\`\`\`${testSection}`);
|
|
68
|
-
}
|
|
69
|
-
//# sourceMappingURL=get.js.map
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MCP Tool Handlers
|
|
3
|
-
*
|
|
4
|
-
* Each handler is a pure function that processes tool arguments
|
|
5
|
-
* and returns an MCP response. Handlers receive a context object
|
|
6
|
-
* that provides access to shared dependencies.
|
|
7
|
-
*/
|
|
8
|
-
export type { CreateRequirementDocumentArgs, ValidateRequirementsArgs, } from "./authoring.js";
|
|
9
|
-
export { handleCreateRequirementDocument, handleValidateRequirements, } from "./authoring.js";
|
|
10
|
-
export type { DebugMcpEnvironmentArgs } from "./debug.js";
|
|
11
|
-
export { handleDebugMcpEnvironment } from "./debug.js";
|
|
12
|
-
export type { GetRequirementArgs } from "./get.js";
|
|
13
|
-
export { handleGetRequirement } from "./get.js";
|
|
14
|
-
export type { ListArgs } from "./list.js";
|
|
15
|
-
export { handleList } from "./list.js";
|
|
16
|
-
export type { PushRequirementsArgs } from "./push.js";
|
|
17
|
-
export { handlePushRequirements } from "./push.js";
|
|
18
|
-
export type { ReportArgs } from "./report.js";
|
|
19
|
-
export { handleReport } from "./report.js";
|
|
20
|
-
export type { ReviewTestArgs, StyleCheckArgs } from "./review.js";
|
|
21
|
-
export { handleReviewTest, handleStyleCheck } from "./review.js";
|
|
22
|
-
export type { SearchRequirementsArgs } from "./search.js";
|
|
23
|
-
export { handleSearchRequirements } from "./search.js";
|
|
24
|
-
export type { GetRequirementsByTestArgs, GetTestsByRequirementArgs, } from "./test-mapping.js";
|
|
25
|
-
export { handleGetRequirementsByTest, handleGetTestsByRequirement, } from "./test-mapping.js";
|
|
26
|
-
export type { HandlerContext, ToolContentItem, ToolHandler, ToolResponse, } from "./types.js";
|
|
27
|
-
export { errorResponse, textResponse } from "./types.js";
|
|
28
|
-
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MCP Tool Handlers
|
|
3
|
-
*
|
|
4
|
-
* Each handler is a pure function that processes tool arguments
|
|
5
|
-
* and returns an MCP response. Handlers receive a context object
|
|
6
|
-
* that provides access to shared dependencies.
|
|
7
|
-
*/
|
|
8
|
-
export { handleCreateRequirementDocument, handleValidateRequirements, } from "./authoring.js";
|
|
9
|
-
// Handlers
|
|
10
|
-
export { handleDebugMcpEnvironment } from "./debug.js";
|
|
11
|
-
export { handleGetRequirement } from "./get.js";
|
|
12
|
-
export { handleList } from "./list.js";
|
|
13
|
-
export { handlePushRequirements } from "./push.js";
|
|
14
|
-
export { handleReport } from "./report.js";
|
|
15
|
-
export { handleReviewTest, handleStyleCheck } from "./review.js";
|
|
16
|
-
export { handleSearchRequirements } from "./search.js";
|
|
17
|
-
export { handleGetRequirementsByTest, handleGetTestsByRequirement, } from "./test-mapping.js";
|
|
18
|
-
export { errorResponse, textResponse } from "./types.js";
|
|
19
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { HandlerContext, ToolResponse } from "./types.js";
|
|
2
|
-
export interface ListArgs {
|
|
3
|
-
untested?: boolean;
|
|
4
|
-
projectId?: string;
|
|
5
|
-
}
|
|
6
|
-
export declare function handleList(args: ListArgs, context: HandlerContext): Promise<ToolResponse>;
|
|
7
|
-
//# sourceMappingURL=list.d.ts.map
|
|
@@ -1,43 +0,0 @@
|
|
|
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;
|
|
5
|
-
const project = await context.getProjectFromDiscovery(projectId);
|
|
6
|
-
const requirements = await context.getRequirements(projectId);
|
|
7
|
-
if (requirements.length === 0) {
|
|
8
|
-
return textResponse(`No requirements found in project: ${project.path}\n\nMake sure .requirements/ directory exists with Markdown files (*.requirements.md).`);
|
|
9
|
-
}
|
|
10
|
-
if (untested) {
|
|
11
|
-
return formatUntested(requirements, project.path);
|
|
12
|
-
}
|
|
13
|
-
return formatAll(requirements);
|
|
14
|
-
}
|
|
15
|
-
function formatAll(requirements) {
|
|
16
|
-
const grouped = new Map();
|
|
17
|
-
for (const req of requirements) {
|
|
18
|
-
const existing = grouped.get(req.rootId) || [];
|
|
19
|
-
existing.push(req);
|
|
20
|
-
grouped.set(req.rootId, existing);
|
|
21
|
-
}
|
|
22
|
-
const formatted = Array.from(grouped.entries())
|
|
23
|
-
.map(([rootId, reqs]) => {
|
|
24
|
-
const root = reqs.find((r) => r.path.length === 0);
|
|
25
|
-
const childCount = reqs.length - 1;
|
|
26
|
-
return `- **${rootId}**: ${root?.content || "(no content)"} (${childCount} children)`;
|
|
27
|
-
})
|
|
28
|
-
.join("\n");
|
|
29
|
-
return textResponse(`Found ${grouped.size} requirement(s) with ${requirements.length} total nodes:\n\n${formatted}`);
|
|
30
|
-
}
|
|
31
|
-
async function formatUntested(requirements, projectPath) {
|
|
32
|
-
const referencedIds = await getReferencedRequirementIds(projectPath);
|
|
33
|
-
const rootRequirements = requirements.filter((r) => r.path.length === 0);
|
|
34
|
-
const untested = rootRequirements.filter((r) => !referencedIds.has(r.id));
|
|
35
|
-
if (untested.length === 0) {
|
|
36
|
-
return textResponse(`All ${rootRequirements.length} requirement(s) have test references — coverage is complete.`);
|
|
37
|
-
}
|
|
38
|
-
const formatted = untested
|
|
39
|
-
.map((r) => `- **${r.id}**: ${r.content}`)
|
|
40
|
-
.join("\n");
|
|
41
|
-
return textResponse(`Found ${untested.length} untested requirement(s) out of ${rootRequirements.length} total:\n\n${formatted}`);
|
|
42
|
-
}
|
|
43
|
-
//# sourceMappingURL=list.js.map
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Push handler for MCP tools
|
|
3
|
-
*
|
|
4
|
-
* Provides requirements push functionality:
|
|
5
|
-
* - push_requirements: Push local requirements to cloud
|
|
6
|
-
*/
|
|
7
|
-
import type { HandlerContext, ToolResponse } from "./types.js";
|
|
8
|
-
/**
|
|
9
|
-
* Arguments for push_requirements tool
|
|
10
|
-
*/
|
|
11
|
-
export interface PushRequirementsArgs {
|
|
12
|
-
filePath?: string;
|
|
13
|
-
confirmed?: boolean;
|
|
14
|
-
projectId?: string;
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Handler for push_requirements tool
|
|
18
|
-
*
|
|
19
|
-
* Requirements covered:
|
|
20
|
-
* - MCP-PUSH-1.0: When not confirmed, no changes are made and a preview of creates/updates is returned
|
|
21
|
-
* - MCP-PUSH-1.1: When confirmed is true, the push executes and returns success message
|
|
22
|
-
* - MCP-PUSH-1.2: When credentials are missing or invalid, an error explains how to authenticate
|
|
23
|
-
* - MCP-PUSH-1.3: When filePath is provided, only that file is pushed (MCP-PUSH-1.3.0: missing file is an error)
|
|
24
|
-
*/
|
|
25
|
-
export declare function handlePushRequirements(args: PushRequirementsArgs, context: HandlerContext): Promise<ToolResponse>;
|
|
26
|
-
//# sourceMappingURL=push.d.ts.map
|
|
@@ -1,232 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Push handler for MCP tools
|
|
3
|
-
*
|
|
4
|
-
* Provides requirements push functionality:
|
|
5
|
-
* - push_requirements: Push local requirements to cloud
|
|
6
|
-
*/
|
|
7
|
-
import { existsSync } from "node:fs";
|
|
8
|
-
import { basename, resolve } from "node:path";
|
|
9
|
-
import { dryRunPush, executePush, parseFilesForPushIndividually, } 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
|
-
/**
|
|
14
|
-
* Handler for push_requirements tool
|
|
15
|
-
*
|
|
16
|
-
* Requirements covered:
|
|
17
|
-
* - MCP-PUSH-1.0: When not confirmed, no changes are made and a preview of creates/updates is returned
|
|
18
|
-
* - MCP-PUSH-1.1: When confirmed is true, the push executes and returns success message
|
|
19
|
-
* - MCP-PUSH-1.2: When credentials are missing or invalid, an error explains how to authenticate
|
|
20
|
-
* - MCP-PUSH-1.3: When filePath is provided, only that file is pushed (MCP-PUSH-1.3.0: missing file is an error)
|
|
21
|
-
*/
|
|
22
|
-
export async function handlePushRequirements(args, context) {
|
|
23
|
-
const { filePath, confirmed = false, projectId } = args;
|
|
24
|
-
// MCP-PUSH-1.2: Get project credentials (throws if not configured)
|
|
25
|
-
let project;
|
|
26
|
-
try {
|
|
27
|
-
project = await context.getProjectFromDiscovery(projectId);
|
|
28
|
-
}
|
|
29
|
-
catch (error) {
|
|
30
|
-
return errorResponse(`Push requires project credentials. Run \`dotrequirements link\` to connect to cloud.\n\nError: ${error instanceof Error ? error.message : String(error)}`);
|
|
31
|
-
}
|
|
32
|
-
// Determine files to push
|
|
33
|
-
let filesToPush;
|
|
34
|
-
// MCP-PUSH-1.3: When filePath is provided, only that file is pushed
|
|
35
|
-
if (filePath) {
|
|
36
|
-
const fullPath = resolve(project.path, filePath);
|
|
37
|
-
if (!existsSync(fullPath)) {
|
|
38
|
-
return errorResponse(`File not found: ${filePath}`);
|
|
39
|
-
}
|
|
40
|
-
filesToPush = [fullPath];
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
// Use same file discovery as CLI - searches entire workspace
|
|
44
|
-
filesToPush = await findRequirementsFiles(project.path);
|
|
45
|
-
if (filesToPush.length === 0) {
|
|
46
|
-
return textResponse("No *.requirements.md files found. Nothing to push.");
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
// Parse files. #45/SYNC-FAIL-4: one invalid file does not abort the whole
|
|
50
|
-
// push — its failure is surfaced in the preview's "Skipped - Invalid"
|
|
51
|
-
// section while valid files still push. Same helper as the CLI command.
|
|
52
|
-
const { parsedFiles, totalRequirements, parseFailures } = parseFilesForPushIndividually(filesToPush);
|
|
53
|
-
// Build credentials
|
|
54
|
-
const credentials = {
|
|
55
|
-
projectId: project.projectId,
|
|
56
|
-
projectSecret: project.projectSecret,
|
|
57
|
-
convexUrl: CONVEX_URL,
|
|
58
|
-
};
|
|
59
|
-
// Run dry run (always, even when confirmed - ensures fresh state).
|
|
60
|
-
// SYNC-FAIL-3: two files claiming the same document ID abort the push
|
|
61
|
-
// before any cloud write — surface that as a clean error result naming
|
|
62
|
-
// both files rather than an unhandled throw.
|
|
63
|
-
let dryRunResult;
|
|
64
|
-
try {
|
|
65
|
-
dryRunResult = await dryRunPush(parsedFiles, credentials);
|
|
66
|
-
}
|
|
67
|
-
catch (error) {
|
|
68
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
69
|
-
if (message.includes("claim the same document ID")) {
|
|
70
|
-
return {
|
|
71
|
-
content: [
|
|
72
|
-
{
|
|
73
|
-
type: "text",
|
|
74
|
-
text: `✗ Push aborted: ${message}`,
|
|
75
|
-
},
|
|
76
|
-
],
|
|
77
|
-
isError: true,
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
throw error;
|
|
81
|
-
}
|
|
82
|
-
// #45: Merge dry-run invalids (missing document section, cloud validation)
|
|
83
|
-
// with local parse failures so both are surfaced together, each named by
|
|
84
|
-
// filename.
|
|
85
|
-
const allInvalid = [
|
|
86
|
-
...dryRunResult.invalid.map(({ file, result }) => ({
|
|
87
|
-
fileName: basename(file.filePath),
|
|
88
|
-
error: result.error ?? "invalid",
|
|
89
|
-
})),
|
|
90
|
-
...parseFailures.map(({ filePath, error }) => ({
|
|
91
|
-
fileName: basename(filePath),
|
|
92
|
-
error,
|
|
93
|
-
})),
|
|
94
|
-
];
|
|
95
|
-
// Check if there's anything to push
|
|
96
|
-
const pushableCount = dryRunResult.updates.length +
|
|
97
|
-
dryRunResult.creates.length +
|
|
98
|
-
dryRunResult.notFound.length;
|
|
99
|
-
if (pushableCount === 0 && allInvalid.length > 0) {
|
|
100
|
-
// Only invalid files
|
|
101
|
-
const invalidList = allInvalid
|
|
102
|
-
.map(({ fileName, error }) => `- ${fileName}: ${error}`)
|
|
103
|
-
.join("\n");
|
|
104
|
-
return {
|
|
105
|
-
content: [
|
|
106
|
-
{
|
|
107
|
-
type: "text",
|
|
108
|
-
text: `✗ No valid documents to push.\n\n**Invalid files:**\n${invalidList}`,
|
|
109
|
-
},
|
|
110
|
-
],
|
|
111
|
-
isError: true,
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
// MCP-PUSH-1.0: When not confirmed, show preview
|
|
115
|
-
if (!confirmed) {
|
|
116
|
-
let summary = `# Push Preview\n\n**Files:** ${parsedFiles.length}\n**Requirements:** ${totalRequirements}\n\n`;
|
|
117
|
-
if (dryRunResult.updates.length > 0) {
|
|
118
|
-
summary += `## Updates (${dryRunResult.updates.length})\n`;
|
|
119
|
-
for (const { file, result } of dryRunResult.updates) {
|
|
120
|
-
const fileName = basename(file.filePath);
|
|
121
|
-
const hasConflict = dryRunResult.conflicts.some((c) => c.item.file === file);
|
|
122
|
-
const conflictNote = hasConflict ? " ⚠️ [cloud changed since pull]" : "";
|
|
123
|
-
const warningNote = result.warning ? ` (${result.warning})` : "";
|
|
124
|
-
summary += `- ~ ${fileName}${conflictNote}${warningNote}\n`;
|
|
125
|
-
}
|
|
126
|
-
summary += "\n";
|
|
127
|
-
}
|
|
128
|
-
if (dryRunResult.creates.length > 0) {
|
|
129
|
-
summary += `## New Documents (${dryRunResult.creates.length})\n`;
|
|
130
|
-
for (const { file, result } of dryRunResult.creates) {
|
|
131
|
-
const fileName = basename(file.filePath);
|
|
132
|
-
const warningNote = result.warning ? ` (${result.warning})` : "";
|
|
133
|
-
summary += `- + ${fileName}${warningNote}\n`;
|
|
134
|
-
}
|
|
135
|
-
summary += "\n";
|
|
136
|
-
}
|
|
137
|
-
if (dryRunResult.notFound.length > 0) {
|
|
138
|
-
summary += `## Not Found in Cloud (${dryRunResult.notFound.length}) - will create new\n`;
|
|
139
|
-
for (const { file, result } of dryRunResult.notFound) {
|
|
140
|
-
const fileName = basename(file.filePath);
|
|
141
|
-
summary += `- ! ${fileName} (ID: ${result.documentId})\n`;
|
|
142
|
-
}
|
|
143
|
-
summary += "\n";
|
|
144
|
-
}
|
|
145
|
-
if (allInvalid.length > 0) {
|
|
146
|
-
summary += `## Skipped - Invalid (${allInvalid.length})\n`;
|
|
147
|
-
for (const { fileName, error } of allInvalid) {
|
|
148
|
-
summary += `- ✗ ${fileName}: ${error}\n`;
|
|
149
|
-
}
|
|
150
|
-
summary += "\n";
|
|
151
|
-
}
|
|
152
|
-
if (dryRunResult.conflicts.length > 0) {
|
|
153
|
-
summary += `⚠️ **Warning:** ${dryRunResult.conflicts.length} file(s) have cloud changes since last pull. Pushing will overwrite those changes.\n\n`;
|
|
154
|
-
}
|
|
155
|
-
summary += `**To proceed:** Call this tool again with \`confirmed: true\``;
|
|
156
|
-
return textResponse(summary);
|
|
157
|
-
}
|
|
158
|
-
// MCP-PUSH-1.1: When confirmed, execute push
|
|
159
|
-
try {
|
|
160
|
-
const result = await executePush(dryRunResult, credentials);
|
|
161
|
-
// SYNC-FAIL-1: a failed push must not masquerade as success
|
|
162
|
-
const failed = result.errors.length;
|
|
163
|
-
const syncedCount = result.created + result.updated;
|
|
164
|
-
let output;
|
|
165
|
-
if (failed > 0 && syncedCount === 0) {
|
|
166
|
-
output = `✗ Push failed: ${failed} document(s) could not be saved.\n\n`;
|
|
167
|
-
}
|
|
168
|
-
else if (failed > 0) {
|
|
169
|
-
output = `⚠ Push incomplete: ${syncedCount} document(s) synced, ${failed} failed.\n\n`;
|
|
170
|
-
}
|
|
171
|
-
else {
|
|
172
|
-
output = "✓ Push complete!\n\n";
|
|
173
|
-
}
|
|
174
|
-
if (result.created > 0) {
|
|
175
|
-
output += `**Created:** ${result.created} document(s)\n`;
|
|
176
|
-
}
|
|
177
|
-
if (result.updated > 0) {
|
|
178
|
-
output += `**Updated:** ${result.updated} document(s)\n`;
|
|
179
|
-
}
|
|
180
|
-
// IMPORT-3: marker failures are loud but never fatal
|
|
181
|
-
if (result.importWarnings.length > 0) {
|
|
182
|
-
output += `\n**Import marker warnings:**\n`;
|
|
183
|
-
for (const { fileName, status } of result.importWarnings) {
|
|
184
|
-
output +=
|
|
185
|
-
status === "already_used"
|
|
186
|
-
? `- ⚠ ${fileName}: import marker already used — requirements were saved as natively authored (they count toward the plan's requirement limit). Re-running codebase-to-spec produces a fresh marker.\n`
|
|
187
|
-
: `- ⚠ ${fileName}: import marker not recognized — the CLI may need updating. Requirements were saved as natively authored (they count toward the plan's requirement limit).\n`;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
if (result.errors.length > 0) {
|
|
191
|
-
output += `\n**Errors:**\n`;
|
|
192
|
-
for (const { fileName, error } of result.errors) {
|
|
193
|
-
output += `- ${fileName}: ${error}\n`;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
// SYNC-FAIL-2.1: cloud save succeeded but the local file couldn't be
|
|
197
|
-
// updated — name the file, say the cloud is fine, and give the recovery
|
|
198
|
-
// step so a retry doesn't mint a duplicate document.
|
|
199
|
-
if (result.writeBackWarnings?.length > 0) {
|
|
200
|
-
output += `\n**Warnings:**\n`;
|
|
201
|
-
for (const warning of result.writeBackWarnings) {
|
|
202
|
-
output +=
|
|
203
|
-
`- ⚠ ${warning.fileName}: saved to the cloud, but the local file could not be updated (${warning.error}). ` +
|
|
204
|
-
`To avoid creating a duplicate, add "id: ${warning.documentId}" under "document:" in the frontmatter of ${warning.filePath}, then push again.\n`;
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
// SYNC-LAND-1: where each synced document lives in the web app
|
|
208
|
-
if (result.synced.length > 0) {
|
|
209
|
-
output += `\n**Review in dot•requirements:**\n`;
|
|
210
|
-
for (const doc of result.synced) {
|
|
211
|
-
output += `- ${doc.fileName}: ${doc.url}\n`;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
// SYNC-FAIL-1.2: total failure is an error result, not a success story
|
|
215
|
-
if (failed > 0 && syncedCount === 0) {
|
|
216
|
-
return { content: [{ type: "text", text: output }], isError: true };
|
|
217
|
-
}
|
|
218
|
-
return textResponse(output);
|
|
219
|
-
}
|
|
220
|
-
catch (error) {
|
|
221
|
-
return {
|
|
222
|
-
content: [
|
|
223
|
-
{
|
|
224
|
-
type: "text",
|
|
225
|
-
text: `✗ Push failed:\n\n${error instanceof Error ? error.message : String(error)}`,
|
|
226
|
-
},
|
|
227
|
-
],
|
|
228
|
-
isError: true,
|
|
229
|
-
};
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
//# sourceMappingURL=push.js.map
|
|
@@ -1,16 +0,0 @@
|
|
|
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
|