bmall-mcp 1.8.1 → 1.8.3
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 -0
- package/dist/tools/requirements/index.d.ts.map +1 -1
- package/dist/tools/requirements/index.js +22 -10
- 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 +84 -21
- package/dist/tools/requirements/requirement-processor.js.map +1 -1
- package/package.json +1 -1
- package/src/tools/requirements/index.ts +27 -10
- package/src/tools/requirements/requirement-processor.ts +94 -21
package/README.md
CHANGED
|
@@ -204,6 +204,19 @@ src/
|
|
|
204
204
|
|
|
205
205
|
## Changelog
|
|
206
206
|
|
|
207
|
+
### v1.8.3 (2026-03-16)
|
|
208
|
+
- ✅ 优化 PDF 读取策略:采用**分两步读取**策略(text + OCR)
|
|
209
|
+
- ✅ 新增 PDF 语言自动检测:根据文件名自动识别中文/英文文档
|
|
210
|
+
- ✅ 完善 PDF 读取提示:建议先读文本层,再用 OCR 提取图片表格
|
|
211
|
+
- ✅ 避免内容遗漏:结合两种方式确保内容完整准确
|
|
212
|
+
|
|
213
|
+
### v1.8.2 (2026-03-16)
|
|
214
|
+
- ✅ 修复工作目录问题:MCP 在根目录 `/` 运行时无法创建 `.specs` 目录
|
|
215
|
+
- ✅ 新增项目根目录自动识别:从输入文件的绝对路径智能提取项目根目录
|
|
216
|
+
- ✅ 优化路径处理:所有输出路径使用绝对路径,避免相对路径的依赖问题
|
|
217
|
+
- ✅ 完善 getProjectRoot 函数:支持从 `.specs` 路径自动识别项目根目录
|
|
218
|
+
- ✅ 更新所有文件操作函数:使用 buildOutputPath 生成绝对路径
|
|
219
|
+
|
|
207
220
|
### v1.8.1 (2026-03-16)
|
|
208
221
|
- ✅ 新增 generate_requirements 工具详细调试日志
|
|
209
222
|
- ✅ 添加环境信息输出(工作目录、平台、主目录)
|
|
@@ -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,6 +1,6 @@
|
|
|
1
1
|
import { McpError } from "@modelcontextprotocol/sdk/types.js";
|
|
2
2
|
import * as os from "os";
|
|
3
|
-
import { processRequirementDocument, tryReadSavedRequirement, tryReadSavedDesign, tryReadSavedBackend } from "./requirement-processor.js";
|
|
3
|
+
import { processRequirementDocument, tryReadSavedRequirement, tryReadSavedDesign, tryReadSavedBackend, getProjectRoot, buildOutputPath } from "./requirement-processor.js";
|
|
4
4
|
/**
|
|
5
5
|
* 调试日志输出
|
|
6
6
|
*/
|
|
@@ -42,9 +42,15 @@ export async function generateRequirements(params) {
|
|
|
42
42
|
return validation.error;
|
|
43
43
|
}
|
|
44
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() });
|
|
45
51
|
// 2.1 优先尝试读取已保存的 origin-requirement.md(智能缓存)
|
|
46
|
-
debugLog(`尝试读取已保存的需求文档`, { featureName: params.featureName });
|
|
47
|
-
const savedContent = await tryReadSavedRequirement(params.featureName);
|
|
52
|
+
debugLog(`尝试读取已保存的需求文档`, { featureName: params.featureName, projectRoot });
|
|
53
|
+
const savedContent = await tryReadSavedRequirement(params.featureName, projectRoot);
|
|
48
54
|
if (!savedContent) {
|
|
49
55
|
debugLog(`未找到缓存,处理需求文档输入`);
|
|
50
56
|
// 2.2 没有缓存,处理需求文档输入
|
|
@@ -71,6 +77,9 @@ export async function generateRequirements(params) {
|
|
|
71
77
|
if (!savedDesign) {
|
|
72
78
|
// 3.2 没有缓存,处理设计稿
|
|
73
79
|
if (params.designSource.type === "file") {
|
|
80
|
+
// 从设计稿路径重新计算项目根目录
|
|
81
|
+
const designProjectRoot = getProjectRoot(params.designSource.path);
|
|
82
|
+
debugLog(`设计稿项目根目录`, { designProjectRoot });
|
|
74
83
|
const designResult = await processRequirementDocument(params.designSource, params.featureName, "design-doc.md");
|
|
75
84
|
if (!designResult.success) {
|
|
76
85
|
return {
|
|
@@ -79,7 +88,7 @@ export async function generateRequirements(params) {
|
|
|
79
88
|
needsAction: designResult.needsAction,
|
|
80
89
|
};
|
|
81
90
|
}
|
|
82
|
-
designSourceInfo =
|
|
91
|
+
designSourceInfo = `**设计稿位置**:${buildOutputPath(params.featureName, "design-doc.md", designProjectRoot)}\n`;
|
|
83
92
|
}
|
|
84
93
|
else if (params.designSource.type === "figma") {
|
|
85
94
|
designSourceInfo = `**设计稿链接**:${params.designSource.url || "Figma 链接未提供"}(请使用 Figma MCP 读取并保存到 .specs/requirements/${params.featureName}/design-doc.md)\n`;
|
|
@@ -87,7 +96,7 @@ export async function generateRequirements(params) {
|
|
|
87
96
|
}
|
|
88
97
|
else {
|
|
89
98
|
// 3.3 使用已保存的设计稿
|
|
90
|
-
designSourceInfo =
|
|
99
|
+
designSourceInfo = `**设计稿位置**:${buildOutputPath(params.featureName, "design-doc.md", projectRoot)}\n`;
|
|
91
100
|
}
|
|
92
101
|
}
|
|
93
102
|
// 4. 处理后端文档
|
|
@@ -97,6 +106,9 @@ export async function generateRequirements(params) {
|
|
|
97
106
|
const savedBackend = await tryReadSavedBackend(params.featureName);
|
|
98
107
|
if (!savedBackend) {
|
|
99
108
|
// 4.2 没有缓存,处理后端文档
|
|
109
|
+
// 从后端文档路径重新计算项目根目录
|
|
110
|
+
const backendProjectRoot = getProjectRoot(params.backendSource.path);
|
|
111
|
+
debugLog(`后端文档项目根目录`, { backendProjectRoot });
|
|
100
112
|
const backendResult = await processRequirementDocument(params.backendSource, params.featureName, "backend-doc.md");
|
|
101
113
|
if (!backendResult.success) {
|
|
102
114
|
return {
|
|
@@ -107,7 +119,7 @@ export async function generateRequirements(params) {
|
|
|
107
119
|
}
|
|
108
120
|
// 4.2.1 文件类型已保存,返回信息
|
|
109
121
|
if (params.backendSource.type === "file") {
|
|
110
|
-
backendSourceInfo =
|
|
122
|
+
backendSourceInfo = `**后端文档位置**:${buildOutputPath(params.featureName, "backend-doc.md", backendProjectRoot)}\n`;
|
|
111
123
|
}
|
|
112
124
|
else if (params.backendSource.type === "url") {
|
|
113
125
|
backendSourceInfo = `**后端文档网页**:${params.backendSource.url || "后端文档网页 URL 未提供"}(请使用 Chrome DevTools MCP 读取并保存到 .specs/requirements/${params.featureName}/backend-doc.md)\n`;
|
|
@@ -115,13 +127,13 @@ export async function generateRequirements(params) {
|
|
|
115
127
|
}
|
|
116
128
|
else {
|
|
117
129
|
// 4.3 使用已保存的后端文档
|
|
118
|
-
backendSourceInfo =
|
|
130
|
+
backendSourceInfo = `**后端文档位置**:${buildOutputPath(params.featureName, "backend-doc.md", projectRoot)}\n`;
|
|
119
131
|
}
|
|
120
132
|
}
|
|
121
133
|
// 5. 生成分析提示词
|
|
122
|
-
const outputPath =
|
|
123
|
-
const originDocPath =
|
|
124
|
-
debugLog(`生成输出路径`, { outputPath, 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 });
|
|
125
137
|
let message = "📝 需求文档已准备完毕\n\n";
|
|
126
138
|
message += `**原始文档位置**:${originDocPath}\n`;
|
|
127
139
|
if (designSourceInfo) {
|
|
@@ -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;AAG9D,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,EACL,0BAA0B,EAC1B,uBAAuB,EAEvB,kBAAkB,EAElB,mBAAmB,
|
|
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":"AAcA,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,
|
|
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,CAiJxB;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"}
|
|
@@ -10,6 +10,33 @@ function debugLog(message, data) {
|
|
|
10
10
|
const logMessage = `[DEBUG ${timestamp}] ${message}${data ? '\n' + JSON.stringify(data, null, 2) : ''}`;
|
|
11
11
|
console.error(logMessage);
|
|
12
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
|
+
}
|
|
13
40
|
/**
|
|
14
41
|
* 步骤 2:处理需求文档输入
|
|
15
42
|
* 这是整体流程的第二步,专门处理需求文档
|
|
@@ -52,26 +79,51 @@ export async function processRequirementDocument(requirementSource, featureName,
|
|
|
52
79
|
debugLog(`文件扩展名`, { ext });
|
|
53
80
|
// 2.2.1 PDF 文件 - 返回读取指令
|
|
54
81
|
if (ext === "pdf") {
|
|
55
|
-
const
|
|
82
|
+
const projectRoot = getProjectRoot(filePath);
|
|
83
|
+
const outputMdPath = buildOutputPath(featureName, outputFileName, projectRoot);
|
|
84
|
+
// 检测语言(简单的启发式规则)
|
|
85
|
+
const fileName = filePath.split('/').pop() || '';
|
|
86
|
+
const isChinese = /[\u4e00-\u9fa5]/.test(fileName);
|
|
87
|
+
const language = isChinese ? 'chi_sim' : 'eng';
|
|
56
88
|
return {
|
|
57
89
|
success: false,
|
|
58
90
|
message: `📄 检测到 PDF 文件
|
|
59
91
|
|
|
60
|
-
|
|
61
|
-
|
|
92
|
+
⚠️ **重要提示**:为确保完整提取 PDF 内容,建议**分两步读取**:
|
|
93
|
+
|
|
94
|
+
**步骤 1:使用 \`read_pdf_text\` 提取文本层**
|
|
95
|
+
- 优势:快速、准确、保留原始格式
|
|
96
|
+
- 保存到:\`${outputMdPath.replace('.md', '-text.md')}\`
|
|
97
|
+
|
|
98
|
+
**步骤 2:使用 \`read_by_ocr\` 提取图片和表格**
|
|
99
|
+
- 优势:可提取图片、表格、扫描件等非文本内容
|
|
100
|
+
- 语言:\`${language}\`(中文文档使用 \`chi_sim\`,英文使用 \`eng\`)
|
|
101
|
+
- DPI:\`300\`(默认值)
|
|
102
|
+
- 保存到:\`${outputMdPath.replace('.md', '-ocr.md')}\`
|
|
103
|
+
|
|
104
|
+
**步骤 3:合并两个文件**
|
|
105
|
+
- 将 \`-text.md\` 和 \`-ocr.md\` 的内容合并
|
|
106
|
+
- 保存到:\`${outputMdPath}\`
|
|
107
|
+
|
|
62
108
|
完成后重新调用本工具`,
|
|
63
109
|
needsAction: {
|
|
64
110
|
type: "read_pdf",
|
|
65
|
-
instruction: `使用 PDF Reader MCP
|
|
111
|
+
instruction: `使用 PDF Reader MCP 分两步读取 ${filePath}:
|
|
112
|
+
1. 先用 read_pdf_text 提取文本层,保存到 ${outputMdPath.replace('.md', '-text.md')}
|
|
113
|
+
2. 再用 read_by_ocr 提取图片表格(language="${language}",dpi=300),保存到 ${outputMdPath.replace('.md', '-ocr.md')}
|
|
114
|
+
3. 合并两个文件内容到 ${outputMdPath}
|
|
115
|
+
这样可以确保内容完整,既保留文本的准确性,又提取了图片表格等视觉内容。`,
|
|
66
116
|
},
|
|
67
117
|
};
|
|
68
118
|
}
|
|
69
119
|
// 2.2.2 Markdown 文件 - 保存到固定位置
|
|
70
120
|
if (ext === "md" || ext === "markdown") {
|
|
71
|
-
const
|
|
121
|
+
const projectRoot = getProjectRoot(filePath);
|
|
122
|
+
const outputMdPath = buildOutputPath(featureName, outputFileName, projectRoot);
|
|
72
123
|
debugLog(`准备保存 Markdown 文件`, {
|
|
73
124
|
inputPath: filePath,
|
|
74
125
|
outputPath: outputMdPath,
|
|
126
|
+
projectRoot,
|
|
75
127
|
isAbsolute: path.isAbsolute(filePath),
|
|
76
128
|
resolvedPath: path.resolve(filePath)
|
|
77
129
|
});
|
|
@@ -79,7 +131,7 @@ export async function processRequirementDocument(requirementSource, featureName,
|
|
|
79
131
|
const content = await fs.readFile(filePath, "utf-8");
|
|
80
132
|
debugLog(`成功读取文件内容`, { contentLength: content.length });
|
|
81
133
|
// 保存到固定位置
|
|
82
|
-
const outputDir =
|
|
134
|
+
const outputDir = path.dirname(outputMdPath);
|
|
83
135
|
debugLog(`准备创建目录`, { outputDir });
|
|
84
136
|
await fs.mkdir(outputDir, { recursive: true });
|
|
85
137
|
debugLog(`目录创建成功`, { outputDir });
|
|
@@ -101,7 +153,10 @@ export async function processRequirementDocument(requirementSource, featureName,
|
|
|
101
153
|
if (!url) {
|
|
102
154
|
throw new McpError(-32602, "网页 URL 不能为空");
|
|
103
155
|
}
|
|
104
|
-
|
|
156
|
+
// 对于 URL,使用当前工作目录作为项目根目录
|
|
157
|
+
const projectRoot = process.cwd();
|
|
158
|
+
const outputMdPath = buildOutputPath(featureName, outputFileName, projectRoot);
|
|
159
|
+
debugLog(`处理网页 URL`, { url, projectRoot, outputMdPath });
|
|
105
160
|
return {
|
|
106
161
|
success: false,
|
|
107
162
|
message: `🌐 检测到网页 URL
|
|
@@ -121,8 +176,10 @@ export async function processRequirementDocument(requirementSource, featureName,
|
|
|
121
176
|
* 尝试读取已保存的需求文档
|
|
122
177
|
* 如果用户之前已经按照指令保存了文件,优先使用已保存的内容
|
|
123
178
|
*/
|
|
124
|
-
export async function tryReadSavedRequirement(featureName) {
|
|
125
|
-
const
|
|
179
|
+
export async function tryReadSavedRequirement(featureName, projectRoot) {
|
|
180
|
+
const root = projectRoot || process.cwd();
|
|
181
|
+
const savedRequirementPath = buildOutputPath(featureName, "origin-requirement.md", root);
|
|
182
|
+
debugLog(`尝试读取已保存的需求文档`, { featureName, projectRoot: root, savedRequirementPath });
|
|
126
183
|
try {
|
|
127
184
|
const savedContent = await fs.readFile(savedRequirementPath, "utf-8");
|
|
128
185
|
if (savedContent) {
|
|
@@ -139,10 +196,11 @@ export async function tryReadSavedRequirement(featureName) {
|
|
|
139
196
|
* 保存设计稿内容到文件
|
|
140
197
|
* 用于保存从 Figma MCP 读取的设计稿内容
|
|
141
198
|
*/
|
|
142
|
-
export async function saveDesignDocument(featureName, designContent) {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
const
|
|
199
|
+
export async function saveDesignDocument(featureName, designContent, projectRoot) {
|
|
200
|
+
const root = projectRoot || process.cwd();
|
|
201
|
+
debugLog(`saveDesignDocument 开始`, { featureName, projectRoot: root });
|
|
202
|
+
const designDocPath = buildOutputPath(featureName, "design-doc.md", root);
|
|
203
|
+
const outputDir = path.dirname(designDocPath);
|
|
146
204
|
debugLog(`设计稿准备创建目录`, { outputDir });
|
|
147
205
|
await fs.mkdir(outputDir, { recursive: true });
|
|
148
206
|
debugLog(`设计稿目录创建成功`);
|
|
@@ -153,8 +211,10 @@ export async function saveDesignDocument(featureName, designContent) {
|
|
|
153
211
|
/**
|
|
154
212
|
* 尝试读取已保存的设计稿文档
|
|
155
213
|
*/
|
|
156
|
-
export async function tryReadSavedDesign(featureName) {
|
|
157
|
-
const
|
|
214
|
+
export async function tryReadSavedDesign(featureName, projectRoot) {
|
|
215
|
+
const root = projectRoot || process.cwd();
|
|
216
|
+
const savedDesignPath = buildOutputPath(featureName, "design-doc.md", root);
|
|
217
|
+
debugLog(`尝试读取已保存的设计稿`, { featureName, projectRoot: root, savedDesignPath });
|
|
158
218
|
try {
|
|
159
219
|
const savedContent = await fs.readFile(savedDesignPath, "utf-8");
|
|
160
220
|
if (savedContent) {
|
|
@@ -171,10 +231,11 @@ export async function tryReadSavedDesign(featureName) {
|
|
|
171
231
|
* 保存后端文档内容到文件
|
|
172
232
|
* 用于保存从 PDF/网页读取的后端文档内容
|
|
173
233
|
*/
|
|
174
|
-
export async function saveBackendDocument(featureName, backendContent) {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
const
|
|
234
|
+
export async function saveBackendDocument(featureName, backendContent, projectRoot) {
|
|
235
|
+
const root = projectRoot || process.cwd();
|
|
236
|
+
debugLog(`saveBackendDocument 开始`, { featureName, projectRoot: root });
|
|
237
|
+
const backendDocPath = buildOutputPath(featureName, "backend-doc.md", root);
|
|
238
|
+
const outputDir = path.dirname(backendDocPath);
|
|
178
239
|
debugLog(`后端文档准备创建目录`, { outputDir });
|
|
179
240
|
await fs.mkdir(outputDir, { recursive: true });
|
|
180
241
|
debugLog(`后端文档目录创建成功`);
|
|
@@ -185,8 +246,10 @@ export async function saveBackendDocument(featureName, backendContent) {
|
|
|
185
246
|
/**
|
|
186
247
|
* 尝试读取已保存的后端文档
|
|
187
248
|
*/
|
|
188
|
-
export async function tryReadSavedBackend(featureName) {
|
|
189
|
-
const
|
|
249
|
+
export async function tryReadSavedBackend(featureName, projectRoot) {
|
|
250
|
+
const root = projectRoot || process.cwd();
|
|
251
|
+
const savedBackendPath = buildOutputPath(featureName, "backend-doc.md", root);
|
|
252
|
+
debugLog(`尝试读取已保存的后端文档`, { featureName, projectRoot: root, savedBackendPath });
|
|
190
253
|
try {
|
|
191
254
|
const savedContent = await fs.readFile(savedBackendPath, "utf-8");
|
|
192
255
|
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;AAClC,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AAEzB;;GAEG;AACH,SAAS,QAAQ,CAAC,OAAe,EAAE,IAAU;IAC3C,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC3C,MAAM,UAAU,GAAG,UAAU,SAAS,KAAK,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;IACxG,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;AAC5B,CAAC;AAwBD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,iBAAgD,EAChD,WAAmB,EACnB,iBAAyB,uBAAuB;IAEhD,QAAQ,CAAC,+BAA+B,EAAE;QACxC,iBAAiB;QACjB,WAAW;QACX,cAAc;QACd,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE;QAClB,QAAQ,EAAE,EAAE,CAAC,QAAQ,EAAE;QACvB,OAAO,EAAE,EAAE,CAAC,OAAO,EAAE;KACtB,CAAC,CAAC;IACH,kBAAkB;IAClB,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACvB,OAAO;YACL,OAAO,EAAE,KAAK;YACd,OAAO,EAAE;;;;;;;8BAOe;YACxB,WAAW,EAAE;gBACX,IAAI,EAAE,iBAAiB;gBACvB,WAAW,EAAE,oFAAoF;aAClG;SACF,CAAC;IACJ,CAAC;IAED,aAAa;IACb,IAAI,iBAAiB,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QACtC,MAAM,QAAQ,GAAG,iBAAiB,CAAC,IAAI,CAAC;QACxC,QAAQ,CAAC,QAAQ,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC;QACjC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,QAAQ,CAAC,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;QACzC,CAAC;QAED,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,WAAW,EAAE,CAAC;QACrD,QAAQ,CAAC,OAAO,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;QAE3B,wBAAwB;QACxB,IAAI,GAAG,KAAK,KAAK,EAAE,CAAC;YAClB,MAAM,YAAY,GAAG,
|
|
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
|
@@ -8,7 +8,9 @@ import {
|
|
|
8
8
|
saveDesignDocument,
|
|
9
9
|
tryReadSavedDesign,
|
|
10
10
|
saveBackendDocument,
|
|
11
|
-
tryReadSavedBackend
|
|
11
|
+
tryReadSavedBackend,
|
|
12
|
+
getProjectRoot,
|
|
13
|
+
buildOutputPath
|
|
12
14
|
} from "./requirement-processor.js";
|
|
13
15
|
|
|
14
16
|
/**
|
|
@@ -100,9 +102,16 @@ export async function generateRequirements(
|
|
|
100
102
|
|
|
101
103
|
// 2. 处理需求文档输入
|
|
102
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
|
+
|
|
103
112
|
// 2.1 优先尝试读取已保存的 origin-requirement.md(智能缓存)
|
|
104
|
-
debugLog(`尝试读取已保存的需求文档`, { featureName: params.featureName });
|
|
105
|
-
const savedContent = await tryReadSavedRequirement(params.featureName);
|
|
113
|
+
debugLog(`尝试读取已保存的需求文档`, { featureName: params.featureName, projectRoot });
|
|
114
|
+
const savedContent = await tryReadSavedRequirement(params.featureName, projectRoot);
|
|
106
115
|
if (!savedContent) {
|
|
107
116
|
debugLog(`未找到缓存,处理需求文档输入`);
|
|
108
117
|
// 2.2 没有缓存,处理需求文档输入
|
|
@@ -135,6 +144,10 @@ export async function generateRequirements(
|
|
|
135
144
|
if (!savedDesign) {
|
|
136
145
|
// 3.2 没有缓存,处理设计稿
|
|
137
146
|
if (params.designSource.type === "file") {
|
|
147
|
+
// 从设计稿路径重新计算项目根目录
|
|
148
|
+
const designProjectRoot = getProjectRoot(params.designSource.path!);
|
|
149
|
+
debugLog(`设计稿项目根目录`, { designProjectRoot });
|
|
150
|
+
|
|
138
151
|
const designResult = await processRequirementDocument(
|
|
139
152
|
params.designSource as any,
|
|
140
153
|
params.featureName,
|
|
@@ -149,13 +162,13 @@ export async function generateRequirements(
|
|
|
149
162
|
};
|
|
150
163
|
}
|
|
151
164
|
|
|
152
|
-
designSourceInfo =
|
|
165
|
+
designSourceInfo = `**设计稿位置**:${buildOutputPath(params.featureName, "design-doc.md", designProjectRoot)}\n`;
|
|
153
166
|
} else if (params.designSource.type === "figma") {
|
|
154
167
|
designSourceInfo = `**设计稿链接**:${params.designSource.url || "Figma 链接未提供"}(请使用 Figma MCP 读取并保存到 .specs/requirements/${params.featureName}/design-doc.md)\n`;
|
|
155
168
|
}
|
|
156
169
|
} else {
|
|
157
170
|
// 3.3 使用已保存的设计稿
|
|
158
|
-
designSourceInfo =
|
|
171
|
+
designSourceInfo = `**设计稿位置**:${buildOutputPath(params.featureName, "design-doc.md", projectRoot)}\n`;
|
|
159
172
|
}
|
|
160
173
|
}
|
|
161
174
|
|
|
@@ -167,6 +180,10 @@ export async function generateRequirements(
|
|
|
167
180
|
|
|
168
181
|
if (!savedBackend) {
|
|
169
182
|
// 4.2 没有缓存,处理后端文档
|
|
183
|
+
// 从后端文档路径重新计算项目根目录
|
|
184
|
+
const backendProjectRoot = getProjectRoot(params.backendSource.path!);
|
|
185
|
+
debugLog(`后端文档项目根目录`, { backendProjectRoot });
|
|
186
|
+
|
|
170
187
|
const backendResult = await processRequirementDocument(
|
|
171
188
|
params.backendSource,
|
|
172
189
|
params.featureName,
|
|
@@ -183,20 +200,20 @@ export async function generateRequirements(
|
|
|
183
200
|
|
|
184
201
|
// 4.2.1 文件类型已保存,返回信息
|
|
185
202
|
if (params.backendSource.type === "file") {
|
|
186
|
-
backendSourceInfo =
|
|
203
|
+
backendSourceInfo = `**后端文档位置**:${buildOutputPath(params.featureName, "backend-doc.md", backendProjectRoot)}\n`;
|
|
187
204
|
} else if (params.backendSource.type === "url") {
|
|
188
205
|
backendSourceInfo = `**后端文档网页**:${params.backendSource.url || "后端文档网页 URL 未提供"}(请使用 Chrome DevTools MCP 读取并保存到 .specs/requirements/${params.featureName}/backend-doc.md)\n`;
|
|
189
206
|
}
|
|
190
207
|
} else {
|
|
191
208
|
// 4.3 使用已保存的后端文档
|
|
192
|
-
backendSourceInfo =
|
|
209
|
+
backendSourceInfo = `**后端文档位置**:${buildOutputPath(params.featureName, "backend-doc.md", projectRoot)}\n`;
|
|
193
210
|
}
|
|
194
211
|
}
|
|
195
212
|
|
|
196
213
|
// 5. 生成分析提示词
|
|
197
|
-
const outputPath =
|
|
198
|
-
const originDocPath =
|
|
199
|
-
debugLog(`生成输出路径`, { outputPath, 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 });
|
|
200
217
|
|
|
201
218
|
let message = "📝 需求文档已准备完毕\n\n";
|
|
202
219
|
message += `**原始文档位置**:${originDocPath}\n`;
|
|
@@ -12,6 +12,35 @@ function debugLog(message: string, data?: any) {
|
|
|
12
12
|
console.error(logMessage);
|
|
13
13
|
}
|
|
14
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
|
+
}
|
|
43
|
+
|
|
15
44
|
interface RequirementSource {
|
|
16
45
|
type: "file" | "url";
|
|
17
46
|
path?: string;
|
|
@@ -83,27 +112,55 @@ export async function processRequirementDocument(
|
|
|
83
112
|
|
|
84
113
|
// 2.2.1 PDF 文件 - 返回读取指令
|
|
85
114
|
if (ext === "pdf") {
|
|
86
|
-
const
|
|
115
|
+
const projectRoot = getProjectRoot(filePath);
|
|
116
|
+
const outputMdPath = buildOutputPath(featureName, outputFileName, projectRoot);
|
|
117
|
+
|
|
118
|
+
// 检测语言(简单的启发式规则)
|
|
119
|
+
const fileName = filePath.split('/').pop() || '';
|
|
120
|
+
const isChinese = /[\u4e00-\u9fa5]/.test(fileName);
|
|
121
|
+
const language = isChinese ? 'chi_sim' : 'eng';
|
|
122
|
+
|
|
87
123
|
return {
|
|
88
124
|
success: false,
|
|
89
125
|
message: `📄 检测到 PDF 文件
|
|
90
126
|
|
|
91
|
-
|
|
92
|
-
|
|
127
|
+
⚠️ **重要提示**:为确保完整提取 PDF 内容,建议**分两步读取**:
|
|
128
|
+
|
|
129
|
+
**步骤 1:使用 \`read_pdf_text\` 提取文本层**
|
|
130
|
+
- 优势:快速、准确、保留原始格式
|
|
131
|
+
- 保存到:\`${outputMdPath.replace('.md', '-text.md')}\`
|
|
132
|
+
|
|
133
|
+
**步骤 2:使用 \`read_by_ocr\` 提取图片和表格**
|
|
134
|
+
- 优势:可提取图片、表格、扫描件等非文本内容
|
|
135
|
+
- 语言:\`${language}\`(中文文档使用 \`chi_sim\`,英文使用 \`eng\`)
|
|
136
|
+
- DPI:\`300\`(默认值)
|
|
137
|
+
- 保存到:\`${outputMdPath.replace('.md', '-ocr.md')}\`
|
|
138
|
+
|
|
139
|
+
**步骤 3:合并两个文件**
|
|
140
|
+
- 将 \`-text.md\` 和 \`-ocr.md\` 的内容合并
|
|
141
|
+
- 保存到:\`${outputMdPath}\`
|
|
142
|
+
|
|
93
143
|
完成后重新调用本工具`,
|
|
94
144
|
needsAction: {
|
|
95
145
|
type: "read_pdf",
|
|
96
|
-
instruction: `使用 PDF Reader MCP
|
|
146
|
+
instruction: `使用 PDF Reader MCP 分两步读取 ${filePath}:
|
|
147
|
+
1. 先用 read_pdf_text 提取文本层,保存到 ${outputMdPath.replace('.md', '-text.md')}
|
|
148
|
+
2. 再用 read_by_ocr 提取图片表格(language="${language}",dpi=300),保存到 ${outputMdPath.replace('.md', '-ocr.md')}
|
|
149
|
+
3. 合并两个文件内容到 ${outputMdPath}
|
|
150
|
+
这样可以确保内容完整,既保留文本的准确性,又提取了图片表格等视觉内容。`,
|
|
97
151
|
},
|
|
98
152
|
};
|
|
99
153
|
}
|
|
100
154
|
|
|
101
155
|
// 2.2.2 Markdown 文件 - 保存到固定位置
|
|
102
156
|
if (ext === "md" || ext === "markdown") {
|
|
103
|
-
const
|
|
157
|
+
const projectRoot = getProjectRoot(filePath);
|
|
158
|
+
const outputMdPath = buildOutputPath(featureName, outputFileName, projectRoot);
|
|
159
|
+
|
|
104
160
|
debugLog(`准备保存 Markdown 文件`, {
|
|
105
161
|
inputPath: filePath,
|
|
106
162
|
outputPath: outputMdPath,
|
|
163
|
+
projectRoot,
|
|
107
164
|
isAbsolute: path.isAbsolute(filePath),
|
|
108
165
|
resolvedPath: path.resolve(filePath)
|
|
109
166
|
});
|
|
@@ -113,7 +170,7 @@ export async function processRequirementDocument(
|
|
|
113
170
|
debugLog(`成功读取文件内容`, { contentLength: content.length });
|
|
114
171
|
|
|
115
172
|
// 保存到固定位置
|
|
116
|
-
const outputDir =
|
|
173
|
+
const outputDir = path.dirname(outputMdPath);
|
|
117
174
|
debugLog(`准备创建目录`, { outputDir });
|
|
118
175
|
await fs.mkdir(outputDir, { recursive: true });
|
|
119
176
|
debugLog(`目录创建成功`, { outputDir });
|
|
@@ -140,7 +197,10 @@ export async function processRequirementDocument(
|
|
|
140
197
|
throw new McpError(-32602, "网页 URL 不能为空");
|
|
141
198
|
}
|
|
142
199
|
|
|
143
|
-
|
|
200
|
+
// 对于 URL,使用当前工作目录作为项目根目录
|
|
201
|
+
const projectRoot = process.cwd();
|
|
202
|
+
const outputMdPath = buildOutputPath(featureName, outputFileName, projectRoot);
|
|
203
|
+
debugLog(`处理网页 URL`, { url, projectRoot, outputMdPath });
|
|
144
204
|
return {
|
|
145
205
|
success: false,
|
|
146
206
|
message: `🌐 检测到网页 URL
|
|
@@ -163,9 +223,12 @@ export async function processRequirementDocument(
|
|
|
163
223
|
* 如果用户之前已经按照指令保存了文件,优先使用已保存的内容
|
|
164
224
|
*/
|
|
165
225
|
export async function tryReadSavedRequirement(
|
|
166
|
-
featureName: string
|
|
226
|
+
featureName: string,
|
|
227
|
+
projectRoot?: string
|
|
167
228
|
): Promise<string | null> {
|
|
168
|
-
const
|
|
229
|
+
const root = projectRoot || process.cwd();
|
|
230
|
+
const savedRequirementPath = buildOutputPath(featureName, "origin-requirement.md", root);
|
|
231
|
+
debugLog(`尝试读取已保存的需求文档`, { featureName, projectRoot: root, savedRequirementPath });
|
|
169
232
|
try {
|
|
170
233
|
const savedContent = await fs.readFile(savedRequirementPath, "utf-8");
|
|
171
234
|
if (savedContent) {
|
|
@@ -184,11 +247,13 @@ export async function tryReadSavedRequirement(
|
|
|
184
247
|
*/
|
|
185
248
|
export async function saveDesignDocument(
|
|
186
249
|
featureName: string,
|
|
187
|
-
designContent: string
|
|
250
|
+
designContent: string,
|
|
251
|
+
projectRoot?: string
|
|
188
252
|
): Promise<void> {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
const
|
|
253
|
+
const root = projectRoot || process.cwd();
|
|
254
|
+
debugLog(`saveDesignDocument 开始`, { featureName, projectRoot: root });
|
|
255
|
+
const designDocPath = buildOutputPath(featureName, "design-doc.md", root);
|
|
256
|
+
const outputDir = path.dirname(designDocPath);
|
|
192
257
|
debugLog(`设计稿准备创建目录`, { outputDir });
|
|
193
258
|
await fs.mkdir(outputDir, { recursive: true });
|
|
194
259
|
debugLog(`设计稿目录创建成功`);
|
|
@@ -201,9 +266,12 @@ export async function saveDesignDocument(
|
|
|
201
266
|
* 尝试读取已保存的设计稿文档
|
|
202
267
|
*/
|
|
203
268
|
export async function tryReadSavedDesign(
|
|
204
|
-
featureName: string
|
|
269
|
+
featureName: string,
|
|
270
|
+
projectRoot?: string
|
|
205
271
|
): Promise<string | null> {
|
|
206
|
-
const
|
|
272
|
+
const root = projectRoot || process.cwd();
|
|
273
|
+
const savedDesignPath = buildOutputPath(featureName, "design-doc.md", root);
|
|
274
|
+
debugLog(`尝试读取已保存的设计稿`, { featureName, projectRoot: root, savedDesignPath });
|
|
207
275
|
try {
|
|
208
276
|
const savedContent = await fs.readFile(savedDesignPath, "utf-8");
|
|
209
277
|
if (savedContent) {
|
|
@@ -222,11 +290,13 @@ export async function tryReadSavedDesign(
|
|
|
222
290
|
*/
|
|
223
291
|
export async function saveBackendDocument(
|
|
224
292
|
featureName: string,
|
|
225
|
-
backendContent: string
|
|
293
|
+
backendContent: string,
|
|
294
|
+
projectRoot?: string
|
|
226
295
|
): Promise<void> {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
const
|
|
296
|
+
const root = projectRoot || process.cwd();
|
|
297
|
+
debugLog(`saveBackendDocument 开始`, { featureName, projectRoot: root });
|
|
298
|
+
const backendDocPath = buildOutputPath(featureName, "backend-doc.md", root);
|
|
299
|
+
const outputDir = path.dirname(backendDocPath);
|
|
230
300
|
debugLog(`后端文档准备创建目录`, { outputDir });
|
|
231
301
|
await fs.mkdir(outputDir, { recursive: true });
|
|
232
302
|
debugLog(`后端文档目录创建成功`);
|
|
@@ -239,9 +309,12 @@ export async function saveBackendDocument(
|
|
|
239
309
|
* 尝试读取已保存的后端文档
|
|
240
310
|
*/
|
|
241
311
|
export async function tryReadSavedBackend(
|
|
242
|
-
featureName: string
|
|
312
|
+
featureName: string,
|
|
313
|
+
projectRoot?: string
|
|
243
314
|
): Promise<string | null> {
|
|
244
|
-
const
|
|
315
|
+
const root = projectRoot || process.cwd();
|
|
316
|
+
const savedBackendPath = buildOutputPath(featureName, "backend-doc.md", root);
|
|
317
|
+
debugLog(`尝试读取已保存的后端文档`, { featureName, projectRoot: root, savedBackendPath });
|
|
245
318
|
try {
|
|
246
319
|
const savedContent = await fs.readFile(savedBackendPath, "utf-8");
|
|
247
320
|
if (savedContent) {
|