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 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":"AAUA,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,CAkID"}
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 { processRequirementDocument, tryReadSavedRequirement, tryReadSavedDesign, tryReadSavedBackend } from "./requirement-processor.js";
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
- const savedContent = await tryReadSavedRequirement(params.featureName);
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 = `**设计稿位置**:.specs/requirements/${params.featureName}/design-doc.md\n`;
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 = `**设计稿位置**:.specs/requirements/${params.featureName}/design-doc.md\n`;
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 = `**后端文档位置**:.specs/requirements/${params.featureName}/backend-doc.md\n`;
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 = `**后端文档位置**:.specs/requirements/${params.featureName}/backend-doc.md\n`;
130
+ backendSourceInfo = `**后端文档位置**:${buildOutputPath(params.featureName, "backend-doc.md", projectRoot)}\n`;
98
131
  }
99
132
  }
100
133
  // 5. 生成分析提示词
101
- const outputPath = `.specs/requirements/${params.featureName}/requirements.md`;
102
- const originDocPath = `.specs/requirements/${params.featureName}/origin-requirement.md`;
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":"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"}
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":"AAGA,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,CA4FxB;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAYxB;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,IAAI,CAAC,CAMf;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAYxB;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,WAAW,EAAE,MAAM,EACnB,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,IAAI,CAAC,CAMf;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAYxB"}
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 outputMdPath = `.specs/requirements/${featureName}/${outputFileName}`;
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 outputMdPath = `.specs/requirements/${featureName}/${outputFileName}`;
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 = outputMdPath.substring(0, outputMdPath.lastIndexOf("/"));
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
- const outputMdPath = `.specs/requirements/${featureName}/${outputFileName}`;
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 savedRequirementPath = `.specs/requirements/${featureName}/origin-requirement.md`;
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 designDocPath = `.specs/requirements/${featureName}/design-doc.md`;
113
- const outputDir = designDocPath.substring(0, designDocPath.lastIndexOf("/"));
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 savedDesignPath = `.specs/requirements/${featureName}/design-doc.md`;
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 backendDocPath = `.specs/requirements/${featureName}/backend-doc.md`;
141
- const outputDir = backendDocPath.substring(0, backendDocPath.lastIndexOf("/"));
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 savedBackendPath = `.specs/requirements/${featureName}/backend-doc.md`;
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;AAwBlC;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,iBAAgD,EAChD,WAAmB,EACnB,iBAAyB,uBAAuB;IAEhD,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,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;QAErD,wBAAwB;QACxB,IAAI,GAAG,KAAK,KAAK,EAAE,CAAC;YAClB,MAAM,YAAY,GAAG,uBAAuB,WAAW,IAAI,cAAc,EAAE,CAAC;YAC5E,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,OAAO,EAAE;;;MAGX,YAAY;WACP;gBACH,WAAW,EAAE;oBACX,IAAI,EAAE,UAAU;oBAChB,WAAW,EAAE,wBAAwB,QAAQ,QAAQ,YAAY,EAAE;iBACpE;aACF,CAAC;QACJ,CAAC;QAED,8BAA8B;QAC9B,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,UAAU,EAAE,CAAC;YACvC,MAAM,YAAY,GAAG,uBAAuB,WAAW,IAAI,cAAc,EAAE,CAAC;YAE5E,iBAAiB;YACjB,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YAErD,UAAU;YACV,MAAM,SAAS,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC,EAAE,YAAY,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;YAC3E,MAAM,EAAE,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC/C,MAAM,EAAE,CAAC,SAAS,CAAC,YAAY,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;YAEnD,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,OAAO;gBACP,OAAO,EAAE,cAAc,YAAY,EAAE;aACtC,CAAC;QACJ,CAAC;QAED,eAAe;QACf,MAAM,IAAI,QAAQ,CAAC,CAAC,KAAK,EAAE,aAAa,GAAG,iBAAiB,CAAC,CAAC;IAChE,CAAC;IAED,eAAe;IACf,IAAI,iBAAiB,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;QACrC,MAAM,GAAG,GAAG,iBAAiB,CAAC,GAAG,CAAC;QAClC,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,MAAM,IAAI,QAAQ,CAAC,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;QAC5C,CAAC;QAED,MAAM,YAAY,GAAG,uBAAuB,WAAW,IAAI,cAAc,EAAE,CAAC;QAC5E,OAAO;YACL,OAAO,EAAE,KAAK;YACd,OAAO,EAAE;;;MAGT,YAAY;WACP;YACL,WAAW,EAAE;gBACX,IAAI,EAAE,UAAU;gBAChB,WAAW,EAAE,6BAA6B,GAAG,QAAQ,YAAY,EAAE;aACpE;SACF,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,QAAQ,CAAC,CAAC,KAAK,EAAE,2CAA2C,CAAC,CAAC;AAC1E,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,WAAmB;IAEnB,MAAM,oBAAoB,GAAG,uBAAuB,WAAW,wBAAwB,CAAC;IACxF,IAAI,CAAC;QACH,MAAM,YAAY,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,oBAAoB,EAAE,OAAO,CAAC,CAAC;QACtE,IAAI,YAAY,EAAE,CAAC;YACjB,OAAO,CAAC,GAAG,CAAC,iBAAiB,oBAAoB,EAAE,CAAC,CAAC;YACrD,OAAO,YAAY,CAAC;QACtB,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,QAAQ;IACV,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,WAAmB,EACnB,aAAqB;IAErB,MAAM,aAAa,GAAG,uBAAuB,WAAW,gBAAgB,CAAC;IACzE,MAAM,SAAS,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC,EAAE,aAAa,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;IAC7E,MAAM,EAAE,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/C,MAAM,EAAE,CAAC,SAAS,CAAC,aAAa,EAAE,aAAa,EAAE,OAAO,CAAC,CAAC;IAC1D,OAAO,CAAC,GAAG,CAAC,cAAc,aAAa,EAAE,CAAC,CAAC;AAC7C,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,WAAmB;IAEnB,MAAM,eAAe,GAAG,uBAAuB,WAAW,gBAAgB,CAAC;IAC3E,IAAI,CAAC;QACH,MAAM,YAAY,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC;QACjE,IAAI,YAAY,EAAE,CAAC;YACjB,OAAO,CAAC,GAAG,CAAC,gBAAgB,eAAe,EAAE,CAAC,CAAC;YAC/C,OAAO,YAAY,CAAC;QACtB,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,QAAQ;IACV,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,WAAmB,EACnB,cAAsB;IAEtB,MAAM,cAAc,GAAG,uBAAuB,WAAW,iBAAiB,CAAC;IAC3E,MAAM,SAAS,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC,EAAE,cAAc,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/E,MAAM,EAAE,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/C,MAAM,EAAE,CAAC,SAAS,CAAC,cAAc,EAAE,cAAc,EAAE,OAAO,CAAC,CAAC;IAC5D,OAAO,CAAC,GAAG,CAAC,eAAe,cAAc,EAAE,CAAC,CAAC;AAC/C,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,WAAmB;IAEnB,MAAM,gBAAgB,GAAG,uBAAuB,WAAW,iBAAiB,CAAC;IAC7E,IAAI,CAAC;QACH,MAAM,YAAY,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;QAClE,IAAI,YAAY,EAAE,CAAC;YACjB,OAAO,CAAC,GAAG,CAAC,iBAAiB,gBAAgB,EAAE,CAAC,CAAC;YACjD,OAAO,YAAY,CAAC;QACtB,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,QAAQ;IACV,CAAC;IACD,OAAO,IAAI,CAAC;AACd,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,6 +1,6 @@
1
1
  {
2
2
  "name": "bmall-mcp",
3
- "version": "1.8.0",
3
+ "version": "1.8.2",
4
4
  "description": "MCP Server for bmall development rules and tools",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -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
- const savedContent = await tryReadSavedRequirement(params.featureName);
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 = `**设计稿位置**:.specs/requirements/${params.featureName}/design-doc.md\n`;
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 = `**设计稿位置**:.specs/requirements/${params.featureName}/design-doc.md\n`;
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 = `**后端文档位置**:.specs/requirements/${params.featureName}/backend-doc.md\n`;
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 = `**后端文档位置**:.specs/requirements/${params.featureName}/backend-doc.md\n`;
209
+ backendSourceInfo = `**后端文档位置**:${buildOutputPath(params.featureName, "backend-doc.md", projectRoot)}\n`;
169
210
  }
170
211
  }
171
212
 
172
213
  // 5. 生成分析提示词
173
- const outputPath = `.specs/requirements/${params.featureName}/requirements.md`;
174
- const originDocPath = `.specs/requirements/${params.featureName}/origin-requirement.md`;
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 outputMdPath = `.specs/requirements/${featureName}/${outputFileName}`;
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 outputMdPath = `.specs/requirements/${featureName}/${outputFileName}`;
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 = outputMdPath.substring(0, outputMdPath.lastIndexOf("/"));
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
- const outputMdPath = `.specs/requirements/${featureName}/${outputFileName}`;
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 savedRequirementPath = `.specs/requirements/${featureName}/origin-requirement.md`;
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 designDocPath = `.specs/requirements/${featureName}/design-doc.md`;
157
- const outputDir = designDocPath.substring(0, designDocPath.lastIndexOf("/"));
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 savedDesignPath = `.specs/requirements/${featureName}/design-doc.md`;
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 backendDocPath = `.specs/requirements/${featureName}/backend-doc.md`;
191
- const outputDir = backendDocPath.substring(0, backendDocPath.lastIndexOf("/"));
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 savedBackendPath = `.specs/requirements/${featureName}/backend-doc.md`;
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) {