bmall-mcp 1.8.0 → 1.8.2
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 +15 -0
- package/dist/tools/requirements/index.d.ts.map +1 -1
- package/dist/tools/requirements/index.js +50 -8
- package/dist/tools/requirements/index.js.map +1 -1
- package/dist/tools/requirements/requirement-processor.d.ts +15 -5
- package/dist/tools/requirements/requirement-processor.d.ts.map +1 -1
- package/dist/tools/requirements/requirement-processor.js +96 -16
- package/dist/tools/requirements/requirement-processor.js.map +1 -1
- package/package.json +1 -1
- package/src/tools/requirements/index.ts +58 -8
- package/src/tools/requirements/requirement-processor.ts +106 -16
package/README.md
CHANGED
|
@@ -204,6 +204,21 @@ src/
|
|
|
204
204
|
|
|
205
205
|
## Changelog
|
|
206
206
|
|
|
207
|
+
### v1.8.2 (2026-03-16)
|
|
208
|
+
- ✅ 修复工作目录问题:MCP 在根目录 `/` 运行时无法创建 `.specs` 目录
|
|
209
|
+
- ✅ 新增项目根目录自动识别:从输入文件的绝对路径智能提取项目根目录
|
|
210
|
+
- ✅ 优化路径处理:所有输出路径使用绝对路径,避免相对路径的依赖问题
|
|
211
|
+
- ✅ 完善 getProjectRoot 函数:支持从 `.specs` 路径自动识别项目根目录
|
|
212
|
+
- ✅ 更新所有文件操作函数:使用 buildOutputPath 生成绝对路径
|
|
213
|
+
|
|
214
|
+
### v1.8.1 (2026-03-16)
|
|
215
|
+
- ✅ 新增 generate_requirements 工具详细调试日志
|
|
216
|
+
- ✅ 添加环境信息输出(工作目录、平台、主目录)
|
|
217
|
+
- ✅ 添加文件路径解析日志(绝对路径/相对路径)
|
|
218
|
+
- ✅ 添加文件操作日志(读取、目录创建、写入)
|
|
219
|
+
- ✅ 添加完整错误堆栈信息
|
|
220
|
+
- ✅ 优化错误诊断能力
|
|
221
|
+
|
|
207
222
|
### v1.8.0 (2026-03-16)
|
|
208
223
|
- ✅ 完善后端文档处理逻辑:添加智能缓存机制
|
|
209
224
|
- ✅ 新增后端文档缓存函数:saveBackendDocument、tryReadSavedBackend
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/requirements/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/requirements/index.ts"],"names":[],"mappings":"AAwBA,UAAU,iBAAiB;IACzB,IAAI,EAAE,MAAM,GAAG,KAAK,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,UAAU,0BAA0B;IAClC,iBAAiB,EAAE,iBAAiB,CAAC;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE;QACb,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC;QACvB,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;IACF,aAAa,CAAC,EAAE;QACd,IAAI,EAAE,MAAM,GAAG,KAAK,CAAC;QACrB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,GAAG,CAAC,EAAE,MAAM,CAAC;KACd,CAAC;CACH;AAmCD,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,0BAA0B,GACjC,OAAO,CAAC;IACT,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE;QACZ,IAAI,EAAE,UAAU,GAAG,UAAU,GAAG,iBAAiB,CAAC;QAClD,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;CACH,CAAC,CAsKD"}
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import { McpError } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
-
import
|
|
2
|
+
import * as os from "os";
|
|
3
|
+
import { processRequirementDocument, tryReadSavedRequirement, tryReadSavedDesign, tryReadSavedBackend, getProjectRoot, buildOutputPath } from "./requirement-processor.js";
|
|
4
|
+
/**
|
|
5
|
+
* 调试日志输出
|
|
6
|
+
*/
|
|
7
|
+
function debugLog(message, data) {
|
|
8
|
+
const timestamp = new Date().toISOString();
|
|
9
|
+
const logMessage = `[DEBUG ${timestamp}] ${message}${data ? '\n' + JSON.stringify(data, null, 2) : ''}`;
|
|
10
|
+
console.error(logMessage);
|
|
11
|
+
}
|
|
3
12
|
/**
|
|
4
13
|
* 验证必填参数
|
|
5
14
|
*/
|
|
@@ -20,6 +29,12 @@ function validateRequiredParams(params) {
|
|
|
20
29
|
return { valid: true };
|
|
21
30
|
}
|
|
22
31
|
export async function generateRequirements(params) {
|
|
32
|
+
debugLog(`generateRequirements 开始`, {
|
|
33
|
+
params,
|
|
34
|
+
cwd: process.cwd(),
|
|
35
|
+
platform: os.platform(),
|
|
36
|
+
homedir: os.homedir()
|
|
37
|
+
});
|
|
23
38
|
try {
|
|
24
39
|
// 1. 验证必填参数
|
|
25
40
|
const validation = validateRequiredParams(params);
|
|
@@ -27,13 +42,22 @@ export async function generateRequirements(params) {
|
|
|
27
42
|
return validation.error;
|
|
28
43
|
}
|
|
29
44
|
// 2. 处理需求文档输入
|
|
45
|
+
// 2.0 计算项目根目录(从输入路径提取)
|
|
46
|
+
let projectRoot = process.cwd();
|
|
47
|
+
if (params.requirementSource.type === "file" && params.requirementSource.path) {
|
|
48
|
+
projectRoot = getProjectRoot(params.requirementSource.path);
|
|
49
|
+
}
|
|
50
|
+
debugLog(`计算项目根目录`, { projectRoot, cwd: process.cwd() });
|
|
30
51
|
// 2.1 优先尝试读取已保存的 origin-requirement.md(智能缓存)
|
|
31
|
-
|
|
52
|
+
debugLog(`尝试读取已保存的需求文档`, { featureName: params.featureName, projectRoot });
|
|
53
|
+
const savedContent = await tryReadSavedRequirement(params.featureName, projectRoot);
|
|
32
54
|
if (!savedContent) {
|
|
55
|
+
debugLog(`未找到缓存,处理需求文档输入`);
|
|
33
56
|
// 2.2 没有缓存,处理需求文档输入
|
|
34
57
|
const requirementResult = await processRequirementDocument(params.requirementSource, params.featureName);
|
|
35
58
|
// 2.2.1 如果需要执行操作(读取 PDF/网页),直接返回
|
|
36
59
|
if (!requirementResult.success) {
|
|
60
|
+
debugLog(`需求文档处理失败`, { requirementResult });
|
|
37
61
|
return {
|
|
38
62
|
success: false,
|
|
39
63
|
message: requirementResult.message,
|
|
@@ -42,6 +66,9 @@ export async function generateRequirements(params) {
|
|
|
42
66
|
}
|
|
43
67
|
// 2.2.2 需求文档已读取并保存(Markdown 文件)
|
|
44
68
|
}
|
|
69
|
+
else {
|
|
70
|
+
debugLog(`找到缓存文档`, { contentLength: savedContent.length });
|
|
71
|
+
}
|
|
45
72
|
// 3. 检查设计稿
|
|
46
73
|
let designSourceInfo = "";
|
|
47
74
|
if (params.designSource) {
|
|
@@ -50,6 +77,9 @@ export async function generateRequirements(params) {
|
|
|
50
77
|
if (!savedDesign) {
|
|
51
78
|
// 3.2 没有缓存,处理设计稿
|
|
52
79
|
if (params.designSource.type === "file") {
|
|
80
|
+
// 从设计稿路径重新计算项目根目录
|
|
81
|
+
const designProjectRoot = getProjectRoot(params.designSource.path);
|
|
82
|
+
debugLog(`设计稿项目根目录`, { designProjectRoot });
|
|
53
83
|
const designResult = await processRequirementDocument(params.designSource, params.featureName, "design-doc.md");
|
|
54
84
|
if (!designResult.success) {
|
|
55
85
|
return {
|
|
@@ -58,7 +88,7 @@ export async function generateRequirements(params) {
|
|
|
58
88
|
needsAction: designResult.needsAction,
|
|
59
89
|
};
|
|
60
90
|
}
|
|
61
|
-
designSourceInfo =
|
|
91
|
+
designSourceInfo = `**设计稿位置**:${buildOutputPath(params.featureName, "design-doc.md", designProjectRoot)}\n`;
|
|
62
92
|
}
|
|
63
93
|
else if (params.designSource.type === "figma") {
|
|
64
94
|
designSourceInfo = `**设计稿链接**:${params.designSource.url || "Figma 链接未提供"}(请使用 Figma MCP 读取并保存到 .specs/requirements/${params.featureName}/design-doc.md)\n`;
|
|
@@ -66,7 +96,7 @@ export async function generateRequirements(params) {
|
|
|
66
96
|
}
|
|
67
97
|
else {
|
|
68
98
|
// 3.3 使用已保存的设计稿
|
|
69
|
-
designSourceInfo =
|
|
99
|
+
designSourceInfo = `**设计稿位置**:${buildOutputPath(params.featureName, "design-doc.md", projectRoot)}\n`;
|
|
70
100
|
}
|
|
71
101
|
}
|
|
72
102
|
// 4. 处理后端文档
|
|
@@ -76,6 +106,9 @@ export async function generateRequirements(params) {
|
|
|
76
106
|
const savedBackend = await tryReadSavedBackend(params.featureName);
|
|
77
107
|
if (!savedBackend) {
|
|
78
108
|
// 4.2 没有缓存,处理后端文档
|
|
109
|
+
// 从后端文档路径重新计算项目根目录
|
|
110
|
+
const backendProjectRoot = getProjectRoot(params.backendSource.path);
|
|
111
|
+
debugLog(`后端文档项目根目录`, { backendProjectRoot });
|
|
79
112
|
const backendResult = await processRequirementDocument(params.backendSource, params.featureName, "backend-doc.md");
|
|
80
113
|
if (!backendResult.success) {
|
|
81
114
|
return {
|
|
@@ -86,7 +119,7 @@ export async function generateRequirements(params) {
|
|
|
86
119
|
}
|
|
87
120
|
// 4.2.1 文件类型已保存,返回信息
|
|
88
121
|
if (params.backendSource.type === "file") {
|
|
89
|
-
backendSourceInfo =
|
|
122
|
+
backendSourceInfo = `**后端文档位置**:${buildOutputPath(params.featureName, "backend-doc.md", backendProjectRoot)}\n`;
|
|
90
123
|
}
|
|
91
124
|
else if (params.backendSource.type === "url") {
|
|
92
125
|
backendSourceInfo = `**后端文档网页**:${params.backendSource.url || "后端文档网页 URL 未提供"}(请使用 Chrome DevTools MCP 读取并保存到 .specs/requirements/${params.featureName}/backend-doc.md)\n`;
|
|
@@ -94,12 +127,13 @@ export async function generateRequirements(params) {
|
|
|
94
127
|
}
|
|
95
128
|
else {
|
|
96
129
|
// 4.3 使用已保存的后端文档
|
|
97
|
-
backendSourceInfo =
|
|
130
|
+
backendSourceInfo = `**后端文档位置**:${buildOutputPath(params.featureName, "backend-doc.md", projectRoot)}\n`;
|
|
98
131
|
}
|
|
99
132
|
}
|
|
100
133
|
// 5. 生成分析提示词
|
|
101
|
-
const outputPath =
|
|
102
|
-
const originDocPath =
|
|
134
|
+
const outputPath = buildOutputPath(params.featureName, "requirements.md", projectRoot);
|
|
135
|
+
const originDocPath = buildOutputPath(params.featureName, "origin-requirement.md", projectRoot);
|
|
136
|
+
debugLog(`生成输出路径`, { outputPath, originDocPath, projectRoot });
|
|
103
137
|
let message = "📝 需求文档已准备完毕\n\n";
|
|
104
138
|
message += `**原始文档位置**:${originDocPath}\n`;
|
|
105
139
|
if (designSourceInfo) {
|
|
@@ -110,6 +144,7 @@ export async function generateRequirements(params) {
|
|
|
110
144
|
}
|
|
111
145
|
message += `**目标输出位置**:${outputPath}\n\n`;
|
|
112
146
|
message += buildAnalysisPrompt(originDocPath, outputPath, params.designSource, params.backendSource);
|
|
147
|
+
debugLog(`返回分析提示词`);
|
|
113
148
|
return {
|
|
114
149
|
success: false,
|
|
115
150
|
message,
|
|
@@ -120,6 +155,13 @@ export async function generateRequirements(params) {
|
|
|
120
155
|
};
|
|
121
156
|
}
|
|
122
157
|
catch (error) {
|
|
158
|
+
debugLog(`发生错误`, {
|
|
159
|
+
error: error instanceof Error ? {
|
|
160
|
+
message: error.message,
|
|
161
|
+
stack: error.stack,
|
|
162
|
+
name: error.name
|
|
163
|
+
} : String(error)
|
|
164
|
+
});
|
|
123
165
|
if (error instanceof McpError) {
|
|
124
166
|
throw error;
|
|
125
167
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/tools/requirements/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oCAAoC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/tools/requirements/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 从输入路径获取项目根目录
|
|
3
|
+
* 如果输入路径是绝对路径,返回该路径的项目根目录
|
|
4
|
+
* 否则使用当前工作目录
|
|
5
|
+
*/
|
|
6
|
+
export declare function getProjectRoot(inputPath: string): string;
|
|
7
|
+
/**
|
|
8
|
+
* 构建输出路径(使用绝对路径)
|
|
9
|
+
*/
|
|
10
|
+
export declare function buildOutputPath(featureName: string, fileName: string, projectRoot: string): string;
|
|
1
11
|
interface RequirementSource {
|
|
2
12
|
type: "file" | "url";
|
|
3
13
|
path?: string;
|
|
@@ -26,24 +36,24 @@ export declare function processRequirementDocument(requirementSource: Requiremen
|
|
|
26
36
|
* 尝试读取已保存的需求文档
|
|
27
37
|
* 如果用户之前已经按照指令保存了文件,优先使用已保存的内容
|
|
28
38
|
*/
|
|
29
|
-
export declare function tryReadSavedRequirement(featureName: string): Promise<string | null>;
|
|
39
|
+
export declare function tryReadSavedRequirement(featureName: string, projectRoot?: string): Promise<string | null>;
|
|
30
40
|
/**
|
|
31
41
|
* 保存设计稿内容到文件
|
|
32
42
|
* 用于保存从 Figma MCP 读取的设计稿内容
|
|
33
43
|
*/
|
|
34
|
-
export declare function saveDesignDocument(featureName: string, designContent: string): Promise<void>;
|
|
44
|
+
export declare function saveDesignDocument(featureName: string, designContent: string, projectRoot?: string): Promise<void>;
|
|
35
45
|
/**
|
|
36
46
|
* 尝试读取已保存的设计稿文档
|
|
37
47
|
*/
|
|
38
|
-
export declare function tryReadSavedDesign(featureName: string): Promise<string | null>;
|
|
48
|
+
export declare function tryReadSavedDesign(featureName: string, projectRoot?: string): Promise<string | null>;
|
|
39
49
|
/**
|
|
40
50
|
* 保存后端文档内容到文件
|
|
41
51
|
* 用于保存从 PDF/网页读取的后端文档内容
|
|
42
52
|
*/
|
|
43
|
-
export declare function saveBackendDocument(featureName: string, backendContent: string): Promise<void>;
|
|
53
|
+
export declare function saveBackendDocument(featureName: string, backendContent: string, projectRoot?: string): Promise<void>;
|
|
44
54
|
/**
|
|
45
55
|
* 尝试读取已保存的后端文档
|
|
46
56
|
*/
|
|
47
|
-
export declare function tryReadSavedBackend(featureName: string): Promise<string | null>;
|
|
57
|
+
export declare function tryReadSavedBackend(featureName: string, projectRoot?: string): Promise<string | null>;
|
|
48
58
|
export {};
|
|
49
59
|
//# sourceMappingURL=requirement-processor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"requirement-processor.d.ts","sourceRoot":"","sources":["../../../src/tools/requirements/requirement-processor.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"requirement-processor.d.ts","sourceRoot":"","sources":["../../../src/tools/requirements/requirement-processor.ts"],"names":[],"mappings":"AAcA;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAexD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAElG;AAED,UAAU,iBAAiB;IACzB,IAAI,EAAE,MAAM,GAAG,KAAK,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,UAAU,aAAa;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE;QACZ,IAAI,EAAE,UAAU,GAAG,UAAU,GAAG,iBAAiB,CAAC;QAClD,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;CACH;AAED,UAAU,iBAAiB;IACzB,IAAI,EAAE,MAAM,GAAG,KAAK,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;GAGG;AACH,wBAAsB,0BAA0B,CAC9C,iBAAiB,EAAE,iBAAiB,GAAG,SAAS,EAChD,WAAW,EAAE,MAAM,EACnB,cAAc,GAAE,MAAgC,GAC/C,OAAO,CAAC,aAAa,CAAC,CAyHxB;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,WAAW,EAAE,MAAM,EACnB,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAcxB;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM,EACrB,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,IAAI,CAAC,CAWf;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,WAAW,EAAE,MAAM,EACnB,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAcxB;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,WAAW,EAAE,MAAM,EACnB,cAAc,EAAE,MAAM,EACtB,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,IAAI,CAAC,CAWf;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,WAAW,EAAE,MAAM,EACnB,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAcxB"}
|
|
@@ -1,10 +1,55 @@
|
|
|
1
1
|
import { McpError } from "@modelcontextprotocol/sdk/types.js";
|
|
2
2
|
import * as fs from "fs/promises";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
import * as os from "os";
|
|
5
|
+
/**
|
|
6
|
+
* 调试日志输出
|
|
7
|
+
*/
|
|
8
|
+
function debugLog(message, data) {
|
|
9
|
+
const timestamp = new Date().toISOString();
|
|
10
|
+
const logMessage = `[DEBUG ${timestamp}] ${message}${data ? '\n' + JSON.stringify(data, null, 2) : ''}`;
|
|
11
|
+
console.error(logMessage);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* 从输入路径获取项目根目录
|
|
15
|
+
* 如果输入路径是绝对路径,返回该路径的项目根目录
|
|
16
|
+
* 否则使用当前工作目录
|
|
17
|
+
*/
|
|
18
|
+
export function getProjectRoot(inputPath) {
|
|
19
|
+
if (path.isAbsolute(inputPath)) {
|
|
20
|
+
// 对于绝对路径,我们假设项目根目录是 .specs 的父目录
|
|
21
|
+
// 例如:/path/to/project/.specs/requirements/xxx
|
|
22
|
+
// 我们需要找到 /path/to/project
|
|
23
|
+
const parts = inputPath.split(path.sep);
|
|
24
|
+
const specsIndex = parts.indexOf('.specs');
|
|
25
|
+
if (specsIndex > 0) {
|
|
26
|
+
return parts.slice(0, specsIndex).join(path.sep);
|
|
27
|
+
}
|
|
28
|
+
// 如果没有找到 .specs,返回文件所在目录
|
|
29
|
+
return path.dirname(inputPath);
|
|
30
|
+
}
|
|
31
|
+
// 对于相对路径,使用当前工作目录
|
|
32
|
+
return process.cwd();
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* 构建输出路径(使用绝对路径)
|
|
36
|
+
*/
|
|
37
|
+
export function buildOutputPath(featureName, fileName, projectRoot) {
|
|
38
|
+
return path.join(projectRoot, '.specs', 'requirements', featureName, fileName);
|
|
39
|
+
}
|
|
3
40
|
/**
|
|
4
41
|
* 步骤 2:处理需求文档输入
|
|
5
42
|
* 这是整体流程的第二步,专门处理需求文档
|
|
6
43
|
*/
|
|
7
44
|
export async function processRequirementDocument(requirementSource, featureName, outputFileName = "origin-requirement.md") {
|
|
45
|
+
debugLog(`processRequirementDocument 开始`, {
|
|
46
|
+
requirementSource,
|
|
47
|
+
featureName,
|
|
48
|
+
outputFileName,
|
|
49
|
+
cwd: process.cwd(),
|
|
50
|
+
platform: os.platform(),
|
|
51
|
+
homedir: os.homedir()
|
|
52
|
+
});
|
|
8
53
|
// 2.1 校验是否提供了需求文档
|
|
9
54
|
if (!requirementSource) {
|
|
10
55
|
return {
|
|
@@ -26,13 +71,16 @@ export async function processRequirementDocument(requirementSource, featureName,
|
|
|
26
71
|
// 2.2 处理文件类型
|
|
27
72
|
if (requirementSource.type === "file") {
|
|
28
73
|
const filePath = requirementSource.path;
|
|
74
|
+
debugLog(`处理文件类型`, { filePath });
|
|
29
75
|
if (!filePath) {
|
|
30
76
|
throw new McpError(-32602, "文件路径不能为空");
|
|
31
77
|
}
|
|
32
78
|
const ext = filePath.split(".").pop()?.toLowerCase();
|
|
79
|
+
debugLog(`文件扩展名`, { ext });
|
|
33
80
|
// 2.2.1 PDF 文件 - 返回读取指令
|
|
34
81
|
if (ext === "pdf") {
|
|
35
|
-
const
|
|
82
|
+
const projectRoot = getProjectRoot(filePath);
|
|
83
|
+
const outputMdPath = buildOutputPath(featureName, outputFileName, projectRoot);
|
|
36
84
|
return {
|
|
37
85
|
success: false,
|
|
38
86
|
message: `📄 检测到 PDF 文件
|
|
@@ -48,13 +96,26 @@ export async function processRequirementDocument(requirementSource, featureName,
|
|
|
48
96
|
}
|
|
49
97
|
// 2.2.2 Markdown 文件 - 保存到固定位置
|
|
50
98
|
if (ext === "md" || ext === "markdown") {
|
|
51
|
-
const
|
|
99
|
+
const projectRoot = getProjectRoot(filePath);
|
|
100
|
+
const outputMdPath = buildOutputPath(featureName, outputFileName, projectRoot);
|
|
101
|
+
debugLog(`准备保存 Markdown 文件`, {
|
|
102
|
+
inputPath: filePath,
|
|
103
|
+
outputPath: outputMdPath,
|
|
104
|
+
projectRoot,
|
|
105
|
+
isAbsolute: path.isAbsolute(filePath),
|
|
106
|
+
resolvedPath: path.resolve(filePath)
|
|
107
|
+
});
|
|
52
108
|
// 读取 Markdown 内容
|
|
53
109
|
const content = await fs.readFile(filePath, "utf-8");
|
|
110
|
+
debugLog(`成功读取文件内容`, { contentLength: content.length });
|
|
54
111
|
// 保存到固定位置
|
|
55
|
-
const outputDir =
|
|
112
|
+
const outputDir = path.dirname(outputMdPath);
|
|
113
|
+
debugLog(`准备创建目录`, { outputDir });
|
|
56
114
|
await fs.mkdir(outputDir, { recursive: true });
|
|
115
|
+
debugLog(`目录创建成功`, { outputDir });
|
|
116
|
+
debugLog(`准备写入文件`, { outputPath: outputMdPath });
|
|
57
117
|
await fs.writeFile(outputMdPath, content, "utf-8");
|
|
118
|
+
debugLog(`文件写入成功`, { outputPath: outputMdPath });
|
|
58
119
|
return {
|
|
59
120
|
success: true,
|
|
60
121
|
content,
|
|
@@ -70,7 +131,10 @@ export async function processRequirementDocument(requirementSource, featureName,
|
|
|
70
131
|
if (!url) {
|
|
71
132
|
throw new McpError(-32602, "网页 URL 不能为空");
|
|
72
133
|
}
|
|
73
|
-
|
|
134
|
+
// 对于 URL,使用当前工作目录作为项目根目录
|
|
135
|
+
const projectRoot = process.cwd();
|
|
136
|
+
const outputMdPath = buildOutputPath(featureName, outputFileName, projectRoot);
|
|
137
|
+
debugLog(`处理网页 URL`, { url, projectRoot, outputMdPath });
|
|
74
138
|
return {
|
|
75
139
|
success: false,
|
|
76
140
|
message: `🌐 检测到网页 URL
|
|
@@ -90,8 +154,10 @@ export async function processRequirementDocument(requirementSource, featureName,
|
|
|
90
154
|
* 尝试读取已保存的需求文档
|
|
91
155
|
* 如果用户之前已经按照指令保存了文件,优先使用已保存的内容
|
|
92
156
|
*/
|
|
93
|
-
export async function tryReadSavedRequirement(featureName) {
|
|
94
|
-
const
|
|
157
|
+
export async function tryReadSavedRequirement(featureName, projectRoot) {
|
|
158
|
+
const root = projectRoot || process.cwd();
|
|
159
|
+
const savedRequirementPath = buildOutputPath(featureName, "origin-requirement.md", root);
|
|
160
|
+
debugLog(`尝试读取已保存的需求文档`, { featureName, projectRoot: root, savedRequirementPath });
|
|
95
161
|
try {
|
|
96
162
|
const savedContent = await fs.readFile(savedRequirementPath, "utf-8");
|
|
97
163
|
if (savedContent) {
|
|
@@ -108,18 +174,25 @@ export async function tryReadSavedRequirement(featureName) {
|
|
|
108
174
|
* 保存设计稿内容到文件
|
|
109
175
|
* 用于保存从 Figma MCP 读取的设计稿内容
|
|
110
176
|
*/
|
|
111
|
-
export async function saveDesignDocument(featureName, designContent) {
|
|
112
|
-
const
|
|
113
|
-
|
|
177
|
+
export async function saveDesignDocument(featureName, designContent, projectRoot) {
|
|
178
|
+
const root = projectRoot || process.cwd();
|
|
179
|
+
debugLog(`saveDesignDocument 开始`, { featureName, projectRoot: root });
|
|
180
|
+
const designDocPath = buildOutputPath(featureName, "design-doc.md", root);
|
|
181
|
+
const outputDir = path.dirname(designDocPath);
|
|
182
|
+
debugLog(`设计稿准备创建目录`, { outputDir });
|
|
114
183
|
await fs.mkdir(outputDir, { recursive: true });
|
|
184
|
+
debugLog(`设计稿目录创建成功`);
|
|
115
185
|
await fs.writeFile(designDocPath, designContent, "utf-8");
|
|
116
186
|
console.log(`✅ 设计稿已保存到: ${designDocPath}`);
|
|
187
|
+
debugLog(`设计稿文件写入成功`, { outputPath: designDocPath });
|
|
117
188
|
}
|
|
118
189
|
/**
|
|
119
190
|
* 尝试读取已保存的设计稿文档
|
|
120
191
|
*/
|
|
121
|
-
export async function tryReadSavedDesign(featureName) {
|
|
122
|
-
const
|
|
192
|
+
export async function tryReadSavedDesign(featureName, projectRoot) {
|
|
193
|
+
const root = projectRoot || process.cwd();
|
|
194
|
+
const savedDesignPath = buildOutputPath(featureName, "design-doc.md", root);
|
|
195
|
+
debugLog(`尝试读取已保存的设计稿`, { featureName, projectRoot: root, savedDesignPath });
|
|
123
196
|
try {
|
|
124
197
|
const savedContent = await fs.readFile(savedDesignPath, "utf-8");
|
|
125
198
|
if (savedContent) {
|
|
@@ -136,18 +209,25 @@ export async function tryReadSavedDesign(featureName) {
|
|
|
136
209
|
* 保存后端文档内容到文件
|
|
137
210
|
* 用于保存从 PDF/网页读取的后端文档内容
|
|
138
211
|
*/
|
|
139
|
-
export async function saveBackendDocument(featureName, backendContent) {
|
|
140
|
-
const
|
|
141
|
-
|
|
212
|
+
export async function saveBackendDocument(featureName, backendContent, projectRoot) {
|
|
213
|
+
const root = projectRoot || process.cwd();
|
|
214
|
+
debugLog(`saveBackendDocument 开始`, { featureName, projectRoot: root });
|
|
215
|
+
const backendDocPath = buildOutputPath(featureName, "backend-doc.md", root);
|
|
216
|
+
const outputDir = path.dirname(backendDocPath);
|
|
217
|
+
debugLog(`后端文档准备创建目录`, { outputDir });
|
|
142
218
|
await fs.mkdir(outputDir, { recursive: true });
|
|
219
|
+
debugLog(`后端文档目录创建成功`);
|
|
143
220
|
await fs.writeFile(backendDocPath, backendContent, "utf-8");
|
|
144
221
|
console.log(`✅ 后端文档已保存到: ${backendDocPath}`);
|
|
222
|
+
debugLog(`后端文档文件写入成功`, { outputPath: backendDocPath });
|
|
145
223
|
}
|
|
146
224
|
/**
|
|
147
225
|
* 尝试读取已保存的后端文档
|
|
148
226
|
*/
|
|
149
|
-
export async function tryReadSavedBackend(featureName) {
|
|
150
|
-
const
|
|
227
|
+
export async function tryReadSavedBackend(featureName, projectRoot) {
|
|
228
|
+
const root = projectRoot || process.cwd();
|
|
229
|
+
const savedBackendPath = buildOutputPath(featureName, "backend-doc.md", root);
|
|
230
|
+
debugLog(`尝试读取已保存的后端文档`, { featureName, projectRoot: root, savedBackendPath });
|
|
151
231
|
try {
|
|
152
232
|
const savedContent = await fs.readFile(savedBackendPath, "utf-8");
|
|
153
233
|
if (savedContent) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"requirement-processor.js","sourceRoot":"","sources":["../../../src/tools/requirements/requirement-processor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oCAAoC,CAAC;AAC9D,OAAO,KAAK,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"requirement-processor.js","sourceRoot":"","sources":["../../../src/tools/requirements/requirement-processor.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,13 +1,27 @@
|
|
|
1
1
|
import { McpError } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
import * as fs from "fs/promises";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
import * as os from "os";
|
|
2
5
|
import {
|
|
3
6
|
processRequirementDocument,
|
|
4
7
|
tryReadSavedRequirement,
|
|
5
8
|
saveDesignDocument,
|
|
6
9
|
tryReadSavedDesign,
|
|
7
10
|
saveBackendDocument,
|
|
8
|
-
tryReadSavedBackend
|
|
11
|
+
tryReadSavedBackend,
|
|
12
|
+
getProjectRoot,
|
|
13
|
+
buildOutputPath
|
|
9
14
|
} from "./requirement-processor.js";
|
|
10
15
|
|
|
16
|
+
/**
|
|
17
|
+
* 调试日志输出
|
|
18
|
+
*/
|
|
19
|
+
function debugLog(message: string, data?: any) {
|
|
20
|
+
const timestamp = new Date().toISOString();
|
|
21
|
+
const logMessage = `[DEBUG ${timestamp}] ${message}${data ? '\n' + JSON.stringify(data, null, 2) : ''}`;
|
|
22
|
+
console.error(logMessage);
|
|
23
|
+
}
|
|
24
|
+
|
|
11
25
|
interface RequirementSource {
|
|
12
26
|
type: "file" | "url";
|
|
13
27
|
path?: string;
|
|
@@ -72,6 +86,13 @@ export async function generateRequirements(
|
|
|
72
86
|
instruction: string;
|
|
73
87
|
};
|
|
74
88
|
}> {
|
|
89
|
+
debugLog(`generateRequirements 开始`, {
|
|
90
|
+
params,
|
|
91
|
+
cwd: process.cwd(),
|
|
92
|
+
platform: os.platform(),
|
|
93
|
+
homedir: os.homedir()
|
|
94
|
+
});
|
|
95
|
+
|
|
75
96
|
try {
|
|
76
97
|
// 1. 验证必填参数
|
|
77
98
|
const validation = validateRequiredParams(params);
|
|
@@ -81,9 +102,18 @@ export async function generateRequirements(
|
|
|
81
102
|
|
|
82
103
|
// 2. 处理需求文档输入
|
|
83
104
|
|
|
105
|
+
// 2.0 计算项目根目录(从输入路径提取)
|
|
106
|
+
let projectRoot = process.cwd();
|
|
107
|
+
if (params.requirementSource.type === "file" && params.requirementSource.path) {
|
|
108
|
+
projectRoot = getProjectRoot(params.requirementSource.path);
|
|
109
|
+
}
|
|
110
|
+
debugLog(`计算项目根目录`, { projectRoot, cwd: process.cwd() });
|
|
111
|
+
|
|
84
112
|
// 2.1 优先尝试读取已保存的 origin-requirement.md(智能缓存)
|
|
85
|
-
|
|
113
|
+
debugLog(`尝试读取已保存的需求文档`, { featureName: params.featureName, projectRoot });
|
|
114
|
+
const savedContent = await tryReadSavedRequirement(params.featureName, projectRoot);
|
|
86
115
|
if (!savedContent) {
|
|
116
|
+
debugLog(`未找到缓存,处理需求文档输入`);
|
|
87
117
|
// 2.2 没有缓存,处理需求文档输入
|
|
88
118
|
const requirementResult = await processRequirementDocument(
|
|
89
119
|
params.requirementSource,
|
|
@@ -92,6 +122,7 @@ export async function generateRequirements(
|
|
|
92
122
|
|
|
93
123
|
// 2.2.1 如果需要执行操作(读取 PDF/网页),直接返回
|
|
94
124
|
if (!requirementResult.success) {
|
|
125
|
+
debugLog(`需求文档处理失败`, { requirementResult });
|
|
95
126
|
return {
|
|
96
127
|
success: false,
|
|
97
128
|
message: requirementResult.message!,
|
|
@@ -100,6 +131,8 @@ export async function generateRequirements(
|
|
|
100
131
|
}
|
|
101
132
|
|
|
102
133
|
// 2.2.2 需求文档已读取并保存(Markdown 文件)
|
|
134
|
+
} else {
|
|
135
|
+
debugLog(`找到缓存文档`, { contentLength: savedContent.length });
|
|
103
136
|
}
|
|
104
137
|
|
|
105
138
|
// 3. 检查设计稿
|
|
@@ -111,6 +144,10 @@ export async function generateRequirements(
|
|
|
111
144
|
if (!savedDesign) {
|
|
112
145
|
// 3.2 没有缓存,处理设计稿
|
|
113
146
|
if (params.designSource.type === "file") {
|
|
147
|
+
// 从设计稿路径重新计算项目根目录
|
|
148
|
+
const designProjectRoot = getProjectRoot(params.designSource.path!);
|
|
149
|
+
debugLog(`设计稿项目根目录`, { designProjectRoot });
|
|
150
|
+
|
|
114
151
|
const designResult = await processRequirementDocument(
|
|
115
152
|
params.designSource as any,
|
|
116
153
|
params.featureName,
|
|
@@ -125,13 +162,13 @@ export async function generateRequirements(
|
|
|
125
162
|
};
|
|
126
163
|
}
|
|
127
164
|
|
|
128
|
-
designSourceInfo =
|
|
165
|
+
designSourceInfo = `**设计稿位置**:${buildOutputPath(params.featureName, "design-doc.md", designProjectRoot)}\n`;
|
|
129
166
|
} else if (params.designSource.type === "figma") {
|
|
130
167
|
designSourceInfo = `**设计稿链接**:${params.designSource.url || "Figma 链接未提供"}(请使用 Figma MCP 读取并保存到 .specs/requirements/${params.featureName}/design-doc.md)\n`;
|
|
131
168
|
}
|
|
132
169
|
} else {
|
|
133
170
|
// 3.3 使用已保存的设计稿
|
|
134
|
-
designSourceInfo =
|
|
171
|
+
designSourceInfo = `**设计稿位置**:${buildOutputPath(params.featureName, "design-doc.md", projectRoot)}\n`;
|
|
135
172
|
}
|
|
136
173
|
}
|
|
137
174
|
|
|
@@ -143,6 +180,10 @@ export async function generateRequirements(
|
|
|
143
180
|
|
|
144
181
|
if (!savedBackend) {
|
|
145
182
|
// 4.2 没有缓存,处理后端文档
|
|
183
|
+
// 从后端文档路径重新计算项目根目录
|
|
184
|
+
const backendProjectRoot = getProjectRoot(params.backendSource.path!);
|
|
185
|
+
debugLog(`后端文档项目根目录`, { backendProjectRoot });
|
|
186
|
+
|
|
146
187
|
const backendResult = await processRequirementDocument(
|
|
147
188
|
params.backendSource,
|
|
148
189
|
params.featureName,
|
|
@@ -159,19 +200,20 @@ export async function generateRequirements(
|
|
|
159
200
|
|
|
160
201
|
// 4.2.1 文件类型已保存,返回信息
|
|
161
202
|
if (params.backendSource.type === "file") {
|
|
162
|
-
backendSourceInfo =
|
|
203
|
+
backendSourceInfo = `**后端文档位置**:${buildOutputPath(params.featureName, "backend-doc.md", backendProjectRoot)}\n`;
|
|
163
204
|
} else if (params.backendSource.type === "url") {
|
|
164
205
|
backendSourceInfo = `**后端文档网页**:${params.backendSource.url || "后端文档网页 URL 未提供"}(请使用 Chrome DevTools MCP 读取并保存到 .specs/requirements/${params.featureName}/backend-doc.md)\n`;
|
|
165
206
|
}
|
|
166
207
|
} else {
|
|
167
208
|
// 4.3 使用已保存的后端文档
|
|
168
|
-
backendSourceInfo =
|
|
209
|
+
backendSourceInfo = `**后端文档位置**:${buildOutputPath(params.featureName, "backend-doc.md", projectRoot)}\n`;
|
|
169
210
|
}
|
|
170
211
|
}
|
|
171
212
|
|
|
172
213
|
// 5. 生成分析提示词
|
|
173
|
-
const outputPath =
|
|
174
|
-
const originDocPath =
|
|
214
|
+
const outputPath = buildOutputPath(params.featureName, "requirements.md", projectRoot);
|
|
215
|
+
const originDocPath = buildOutputPath(params.featureName, "origin-requirement.md", projectRoot);
|
|
216
|
+
debugLog(`生成输出路径`, { outputPath, originDocPath, projectRoot });
|
|
175
217
|
|
|
176
218
|
let message = "📝 需求文档已准备完毕\n\n";
|
|
177
219
|
message += `**原始文档位置**:${originDocPath}\n`;
|
|
@@ -184,6 +226,7 @@ export async function generateRequirements(
|
|
|
184
226
|
message += `**目标输出位置**:${outputPath}\n\n`;
|
|
185
227
|
message += buildAnalysisPrompt(originDocPath, outputPath, params.designSource, params.backendSource);
|
|
186
228
|
|
|
229
|
+
debugLog(`返回分析提示词`);
|
|
187
230
|
return {
|
|
188
231
|
success: false,
|
|
189
232
|
message,
|
|
@@ -193,6 +236,13 @@ export async function generateRequirements(
|
|
|
193
236
|
},
|
|
194
237
|
};
|
|
195
238
|
} catch (error) {
|
|
239
|
+
debugLog(`发生错误`, {
|
|
240
|
+
error: error instanceof Error ? {
|
|
241
|
+
message: error.message,
|
|
242
|
+
stack: error.stack,
|
|
243
|
+
name: error.name
|
|
244
|
+
} : String(error)
|
|
245
|
+
});
|
|
196
246
|
if (error instanceof McpError) {
|
|
197
247
|
throw error;
|
|
198
248
|
}
|
|
@@ -1,5 +1,45 @@
|
|
|
1
1
|
import { McpError } from "@modelcontextprotocol/sdk/types.js";
|
|
2
2
|
import * as fs from "fs/promises";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
import * as os from "os";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 调试日志输出
|
|
8
|
+
*/
|
|
9
|
+
function debugLog(message: string, data?: any) {
|
|
10
|
+
const timestamp = new Date().toISOString();
|
|
11
|
+
const logMessage = `[DEBUG ${timestamp}] ${message}${data ? '\n' + JSON.stringify(data, null, 2) : ''}`;
|
|
12
|
+
console.error(logMessage);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 从输入路径获取项目根目录
|
|
17
|
+
* 如果输入路径是绝对路径,返回该路径的项目根目录
|
|
18
|
+
* 否则使用当前工作目录
|
|
19
|
+
*/
|
|
20
|
+
export function getProjectRoot(inputPath: string): string {
|
|
21
|
+
if (path.isAbsolute(inputPath)) {
|
|
22
|
+
// 对于绝对路径,我们假设项目根目录是 .specs 的父目录
|
|
23
|
+
// 例如:/path/to/project/.specs/requirements/xxx
|
|
24
|
+
// 我们需要找到 /path/to/project
|
|
25
|
+
const parts = inputPath.split(path.sep);
|
|
26
|
+
const specsIndex = parts.indexOf('.specs');
|
|
27
|
+
if (specsIndex > 0) {
|
|
28
|
+
return parts.slice(0, specsIndex).join(path.sep);
|
|
29
|
+
}
|
|
30
|
+
// 如果没有找到 .specs,返回文件所在目录
|
|
31
|
+
return path.dirname(inputPath);
|
|
32
|
+
}
|
|
33
|
+
// 对于相对路径,使用当前工作目录
|
|
34
|
+
return process.cwd();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 构建输出路径(使用绝对路径)
|
|
39
|
+
*/
|
|
40
|
+
export function buildOutputPath(featureName: string, fileName: string, projectRoot: string): string {
|
|
41
|
+
return path.join(projectRoot, '.specs', 'requirements', featureName, fileName);
|
|
42
|
+
}
|
|
3
43
|
|
|
4
44
|
interface RequirementSource {
|
|
5
45
|
type: "file" | "url";
|
|
@@ -32,6 +72,14 @@ export async function processRequirementDocument(
|
|
|
32
72
|
featureName: string,
|
|
33
73
|
outputFileName: string = "origin-requirement.md"
|
|
34
74
|
): Promise<ProcessResult> {
|
|
75
|
+
debugLog(`processRequirementDocument 开始`, {
|
|
76
|
+
requirementSource,
|
|
77
|
+
featureName,
|
|
78
|
+
outputFileName,
|
|
79
|
+
cwd: process.cwd(),
|
|
80
|
+
platform: os.platform(),
|
|
81
|
+
homedir: os.homedir()
|
|
82
|
+
});
|
|
35
83
|
// 2.1 校验是否提供了需求文档
|
|
36
84
|
if (!requirementSource) {
|
|
37
85
|
return {
|
|
@@ -54,15 +102,18 @@ export async function processRequirementDocument(
|
|
|
54
102
|
// 2.2 处理文件类型
|
|
55
103
|
if (requirementSource.type === "file") {
|
|
56
104
|
const filePath = requirementSource.path;
|
|
105
|
+
debugLog(`处理文件类型`, { filePath });
|
|
57
106
|
if (!filePath) {
|
|
58
107
|
throw new McpError(-32602, "文件路径不能为空");
|
|
59
108
|
}
|
|
60
109
|
|
|
61
110
|
const ext = filePath.split(".").pop()?.toLowerCase();
|
|
111
|
+
debugLog(`文件扩展名`, { ext });
|
|
62
112
|
|
|
63
113
|
// 2.2.1 PDF 文件 - 返回读取指令
|
|
64
114
|
if (ext === "pdf") {
|
|
65
|
-
const
|
|
115
|
+
const projectRoot = getProjectRoot(filePath);
|
|
116
|
+
const outputMdPath = buildOutputPath(featureName, outputFileName, projectRoot);
|
|
66
117
|
return {
|
|
67
118
|
success: false,
|
|
68
119
|
message: `📄 检测到 PDF 文件
|
|
@@ -79,15 +130,30 @@ export async function processRequirementDocument(
|
|
|
79
130
|
|
|
80
131
|
// 2.2.2 Markdown 文件 - 保存到固定位置
|
|
81
132
|
if (ext === "md" || ext === "markdown") {
|
|
82
|
-
const
|
|
133
|
+
const projectRoot = getProjectRoot(filePath);
|
|
134
|
+
const outputMdPath = buildOutputPath(featureName, outputFileName, projectRoot);
|
|
135
|
+
|
|
136
|
+
debugLog(`准备保存 Markdown 文件`, {
|
|
137
|
+
inputPath: filePath,
|
|
138
|
+
outputPath: outputMdPath,
|
|
139
|
+
projectRoot,
|
|
140
|
+
isAbsolute: path.isAbsolute(filePath),
|
|
141
|
+
resolvedPath: path.resolve(filePath)
|
|
142
|
+
});
|
|
83
143
|
|
|
84
144
|
// 读取 Markdown 内容
|
|
85
145
|
const content = await fs.readFile(filePath, "utf-8");
|
|
146
|
+
debugLog(`成功读取文件内容`, { contentLength: content.length });
|
|
86
147
|
|
|
87
148
|
// 保存到固定位置
|
|
88
|
-
const outputDir =
|
|
149
|
+
const outputDir = path.dirname(outputMdPath);
|
|
150
|
+
debugLog(`准备创建目录`, { outputDir });
|
|
89
151
|
await fs.mkdir(outputDir, { recursive: true });
|
|
152
|
+
debugLog(`目录创建成功`, { outputDir });
|
|
153
|
+
|
|
154
|
+
debugLog(`准备写入文件`, { outputPath: outputMdPath });
|
|
90
155
|
await fs.writeFile(outputMdPath, content, "utf-8");
|
|
156
|
+
debugLog(`文件写入成功`, { outputPath: outputMdPath });
|
|
91
157
|
|
|
92
158
|
return {
|
|
93
159
|
success: true,
|
|
@@ -107,7 +173,10 @@ export async function processRequirementDocument(
|
|
|
107
173
|
throw new McpError(-32602, "网页 URL 不能为空");
|
|
108
174
|
}
|
|
109
175
|
|
|
110
|
-
|
|
176
|
+
// 对于 URL,使用当前工作目录作为项目根目录
|
|
177
|
+
const projectRoot = process.cwd();
|
|
178
|
+
const outputMdPath = buildOutputPath(featureName, outputFileName, projectRoot);
|
|
179
|
+
debugLog(`处理网页 URL`, { url, projectRoot, outputMdPath });
|
|
111
180
|
return {
|
|
112
181
|
success: false,
|
|
113
182
|
message: `🌐 检测到网页 URL
|
|
@@ -130,9 +199,12 @@ export async function processRequirementDocument(
|
|
|
130
199
|
* 如果用户之前已经按照指令保存了文件,优先使用已保存的内容
|
|
131
200
|
*/
|
|
132
201
|
export async function tryReadSavedRequirement(
|
|
133
|
-
featureName: string
|
|
202
|
+
featureName: string,
|
|
203
|
+
projectRoot?: string
|
|
134
204
|
): Promise<string | null> {
|
|
135
|
-
const
|
|
205
|
+
const root = projectRoot || process.cwd();
|
|
206
|
+
const savedRequirementPath = buildOutputPath(featureName, "origin-requirement.md", root);
|
|
207
|
+
debugLog(`尝试读取已保存的需求文档`, { featureName, projectRoot: root, savedRequirementPath });
|
|
136
208
|
try {
|
|
137
209
|
const savedContent = await fs.readFile(savedRequirementPath, "utf-8");
|
|
138
210
|
if (savedContent) {
|
|
@@ -151,22 +223,31 @@ export async function tryReadSavedRequirement(
|
|
|
151
223
|
*/
|
|
152
224
|
export async function saveDesignDocument(
|
|
153
225
|
featureName: string,
|
|
154
|
-
designContent: string
|
|
226
|
+
designContent: string,
|
|
227
|
+
projectRoot?: string
|
|
155
228
|
): Promise<void> {
|
|
156
|
-
const
|
|
157
|
-
|
|
229
|
+
const root = projectRoot || process.cwd();
|
|
230
|
+
debugLog(`saveDesignDocument 开始`, { featureName, projectRoot: root });
|
|
231
|
+
const designDocPath = buildOutputPath(featureName, "design-doc.md", root);
|
|
232
|
+
const outputDir = path.dirname(designDocPath);
|
|
233
|
+
debugLog(`设计稿准备创建目录`, { outputDir });
|
|
158
234
|
await fs.mkdir(outputDir, { recursive: true });
|
|
235
|
+
debugLog(`设计稿目录创建成功`);
|
|
159
236
|
await fs.writeFile(designDocPath, designContent, "utf-8");
|
|
160
237
|
console.log(`✅ 设计稿已保存到: ${designDocPath}`);
|
|
238
|
+
debugLog(`设计稿文件写入成功`, { outputPath: designDocPath });
|
|
161
239
|
}
|
|
162
240
|
|
|
163
241
|
/**
|
|
164
242
|
* 尝试读取已保存的设计稿文档
|
|
165
243
|
*/
|
|
166
244
|
export async function tryReadSavedDesign(
|
|
167
|
-
featureName: string
|
|
245
|
+
featureName: string,
|
|
246
|
+
projectRoot?: string
|
|
168
247
|
): Promise<string | null> {
|
|
169
|
-
const
|
|
248
|
+
const root = projectRoot || process.cwd();
|
|
249
|
+
const savedDesignPath = buildOutputPath(featureName, "design-doc.md", root);
|
|
250
|
+
debugLog(`尝试读取已保存的设计稿`, { featureName, projectRoot: root, savedDesignPath });
|
|
170
251
|
try {
|
|
171
252
|
const savedContent = await fs.readFile(savedDesignPath, "utf-8");
|
|
172
253
|
if (savedContent) {
|
|
@@ -185,22 +266,31 @@ export async function tryReadSavedDesign(
|
|
|
185
266
|
*/
|
|
186
267
|
export async function saveBackendDocument(
|
|
187
268
|
featureName: string,
|
|
188
|
-
backendContent: string
|
|
269
|
+
backendContent: string,
|
|
270
|
+
projectRoot?: string
|
|
189
271
|
): Promise<void> {
|
|
190
|
-
const
|
|
191
|
-
|
|
272
|
+
const root = projectRoot || process.cwd();
|
|
273
|
+
debugLog(`saveBackendDocument 开始`, { featureName, projectRoot: root });
|
|
274
|
+
const backendDocPath = buildOutputPath(featureName, "backend-doc.md", root);
|
|
275
|
+
const outputDir = path.dirname(backendDocPath);
|
|
276
|
+
debugLog(`后端文档准备创建目录`, { outputDir });
|
|
192
277
|
await fs.mkdir(outputDir, { recursive: true });
|
|
278
|
+
debugLog(`后端文档目录创建成功`);
|
|
193
279
|
await fs.writeFile(backendDocPath, backendContent, "utf-8");
|
|
194
280
|
console.log(`✅ 后端文档已保存到: ${backendDocPath}`);
|
|
281
|
+
debugLog(`后端文档文件写入成功`, { outputPath: backendDocPath });
|
|
195
282
|
}
|
|
196
283
|
|
|
197
284
|
/**
|
|
198
285
|
* 尝试读取已保存的后端文档
|
|
199
286
|
*/
|
|
200
287
|
export async function tryReadSavedBackend(
|
|
201
|
-
featureName: string
|
|
288
|
+
featureName: string,
|
|
289
|
+
projectRoot?: string
|
|
202
290
|
): Promise<string | null> {
|
|
203
|
-
const
|
|
291
|
+
const root = projectRoot || process.cwd();
|
|
292
|
+
const savedBackendPath = buildOutputPath(featureName, "backend-doc.md", root);
|
|
293
|
+
debugLog(`尝试读取已保存的后端文档`, { featureName, projectRoot: root, savedBackendPath });
|
|
204
294
|
try {
|
|
205
295
|
const savedContent = await fs.readFile(savedBackendPath, "utf-8");
|
|
206
296
|
if (savedContent) {
|