@maxenlin/mcp-zentao-11-3 1.0.0-patch.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,1338 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { z } from "zod";
5
+ import { ZentaoLegacyAPI } from './zentaoLegacyApi.js';
6
+ import { loadConfig } from './config.js';
7
+ import { downloadImages, buildImageContent } from './utils/imageDownloader.js';
8
+ import { ZentaoError, ErrorCode, createError } from './errors.js';
9
+ import { formatStoryAsMarkdown, formatBugAsMarkdown, formatTaskAsMarkdown, generateStorySummary, generateBugSummary } from './utils/formatter.js';
10
+ import { analyzeStoryComplexity, analyzeBugPriority, analyzeTaskWorkload } from './utils/analyzer.js';
11
+ import { suggestNextActionsForStory, suggestNextActionsForBug, suggestNextActionsForTask, formatSuggestionsAsMarkdown } from './utils/suggestions.js';
12
+ // Create an MCP server
13
+ const server = new McpServer({
14
+ name: "Zentao 11.3 Legacy API",
15
+ version: "1.0.0"
16
+ });
17
+ // Initialize ZentaoAPI instance (只支持 legacy)
18
+ let zentaoApi = null;
19
+ /**
20
+ * 自动初始化 Zentao API(如果未初始化)
21
+ */
22
+ async function ensureInitialized() {
23
+ if (zentaoApi) {
24
+ return; // 已经初始化,直接返回
25
+ }
26
+ // 尝试加载配置并初始化
27
+ const config = loadConfig();
28
+ if (!config) {
29
+ throw createError(ErrorCode.CONFIG_ERROR, "未找到配置信息。请设置环境变量 ZENTAO_URL、ZENTAO_USERNAME、ZENTAO_PASSWORD,或创建配置文件。");
30
+ }
31
+ zentaoApi = new ZentaoLegacyAPI(config);
32
+ }
33
+ // Add Zentao configuration tool(保留用于手动初始化,但所有工具都会自动初始化)
34
+ server.tool("initZentao", {}, async ({}) => {
35
+ await ensureInitialized();
36
+ const config = loadConfig();
37
+ if (!config) {
38
+ throw createError(ErrorCode.CONFIG_ERROR, "No configuration found. Please provide complete Zentao configuration.");
39
+ }
40
+ return {
41
+ content: [{ type: "text", text: JSON.stringify(config, null, 2) }]
42
+ };
43
+ });
44
+ // Add getConfig tool
45
+ server.tool("getConfig", {}, async () => {
46
+ try {
47
+ const config = loadConfig();
48
+ if (!config) {
49
+ throw createError(ErrorCode.CONFIG_ERROR, "No configuration found. Please initialize Zentao first.");
50
+ }
51
+ const safeConfig = {
52
+ ...config,
53
+ password: '***'
54
+ };
55
+ return {
56
+ content: [{
57
+ type: "text",
58
+ text: JSON.stringify(safeConfig, null, 2)
59
+ }]
60
+ };
61
+ }
62
+ catch (error) {
63
+ return {
64
+ content: [{
65
+ type: "text",
66
+ text: JSON.stringify({
67
+ success: false,
68
+ error: error instanceof Error ? error.message : String(error)
69
+ }, null, 2)
70
+ }]
71
+ };
72
+ }
73
+ });
74
+ // Add getMyTasks tool
75
+ server.tool("getMyTasks", {
76
+ status: z.enum(['wait', 'doing', 'done', 'all']).optional()
77
+ }, async ({ status }) => {
78
+ await ensureInitialized();
79
+ try {
80
+ // Legacy API 的 getMyTasks 不接受参数,返回所有任务
81
+ const tasks = await zentaoApi.getMyTasks();
82
+ // 如果需要过滤状态,在本地过滤
83
+ let filteredTasks = tasks;
84
+ if (status && status !== 'all') {
85
+ filteredTasks = tasks.filter(task => task.status === status);
86
+ }
87
+ return {
88
+ content: [{ type: "text", text: JSON.stringify(filteredTasks, null, 2) }]
89
+ };
90
+ }
91
+ catch (error) {
92
+ if (error instanceof ZentaoError) {
93
+ throw error;
94
+ }
95
+ throw createError(ErrorCode.API_ERROR, `获取我的任务列表失败: ${error instanceof Error ? error.message : String(error)}`, undefined, error);
96
+ }
97
+ });
98
+ // Add getTaskDetail tool
99
+ server.tool("getTaskDetail", {
100
+ taskId: z.number()
101
+ }, async ({ taskId }) => {
102
+ await ensureInitialized();
103
+ try {
104
+ const task = await zentaoApi.getTaskDetail(taskId);
105
+ return {
106
+ content: [{ type: "text", text: JSON.stringify(task, null, 2) }]
107
+ };
108
+ }
109
+ catch (error) {
110
+ if (error instanceof ZentaoError) {
111
+ throw error;
112
+ }
113
+ throw createError(ErrorCode.API_ERROR, `获取任务 ${taskId} 详情失败: ${error instanceof Error ? error.message : String(error)}`, undefined, error);
114
+ }
115
+ });
116
+ // Add getProducts tool
117
+ server.tool("getProducts", {}, async () => {
118
+ await ensureInitialized();
119
+ try {
120
+ const products = await zentaoApi.getProducts();
121
+ return {
122
+ content: [{ type: "text", text: JSON.stringify(products, null, 2) }]
123
+ };
124
+ }
125
+ catch (error) {
126
+ if (error instanceof ZentaoError) {
127
+ throw error;
128
+ }
129
+ throw createError(ErrorCode.API_ERROR, `获取产品列表失败: ${error instanceof Error ? error.message : String(error)}`, undefined, error);
130
+ }
131
+ });
132
+ // Add getMyBugs tool
133
+ server.tool("getMyBugs", {
134
+ status: z.enum(['active', 'resolved', 'closed', 'all']).optional(),
135
+ productId: z.number().optional()
136
+ }, async ({ status, productId }) => {
137
+ await ensureInitialized();
138
+ try {
139
+ // Legacy API 的 getMyBugs 不接受参数,返回所有Bug
140
+ const bugs = await zentaoApi.getMyBugs();
141
+ // 如果需要过滤,在本地过滤
142
+ let filteredBugs = bugs;
143
+ if (status && status !== 'all') {
144
+ filteredBugs = bugs.filter(bug => bug.status === status);
145
+ }
146
+ if (productId) {
147
+ // 注意:Legacy API 返回的 Bug 可能没有 productId 字段,需要从详情获取
148
+ // 这里先简单返回所有,如果需要按产品过滤,需要调用 getBugDetail
149
+ }
150
+ return {
151
+ content: [{ type: "text", text: JSON.stringify(filteredBugs, null, 2) }]
152
+ };
153
+ }
154
+ catch (error) {
155
+ if (error instanceof ZentaoError) {
156
+ throw error;
157
+ }
158
+ throw createError(ErrorCode.API_ERROR, `获取我的 Bug 列表失败: ${error instanceof Error ? error.message : String(error)}`, undefined, error);
159
+ }
160
+ });
161
+ // Add getBugDetail tool
162
+ server.tool("getBugDetail", {
163
+ bugId: z.number(),
164
+ downloadImages: z.boolean().optional().default(true)
165
+ }, async ({ bugId, downloadImages: shouldDownloadImages = true }) => {
166
+ await ensureInitialized();
167
+ try {
168
+ const bug = await zentaoApi.getBugDetail(bugId);
169
+ const result = {
170
+ ...bug,
171
+ images: [],
172
+ fileIds: [],
173
+ hasImages: false,
174
+ hasFiles: false
175
+ };
176
+ // 提取图片和文件信息
177
+ if (bug.steps) {
178
+ result.images = zentaoApi.extractImageUrls(bug.steps);
179
+ result.fileIds = zentaoApi.extractFileIds(bug.steps);
180
+ result.hasImages = result.images.length > 0;
181
+ result.hasFiles = result.fileIds.length > 0;
182
+ }
183
+ // 下载图片(并行)
184
+ if (shouldDownloadImages && result.images.length > 0) {
185
+ result.downloadedImages = await downloadImages(zentaoApi, result.images, true);
186
+ }
187
+ // 构建返回内容,包含文本和图片
188
+ const content = [
189
+ { type: "text", text: JSON.stringify(result, null, 2) }
190
+ ];
191
+ // 添加图片内容(使用MCP协议的image类型)
192
+ if (result.downloadedImages && result.downloadedImages.length > 0) {
193
+ const imageContent = buildImageContent(result.downloadedImages, 'bug', bugId);
194
+ content.push(...imageContent);
195
+ }
196
+ return {
197
+ content: content
198
+ };
199
+ }
200
+ catch (error) {
201
+ if (error instanceof ZentaoError) {
202
+ throw error;
203
+ }
204
+ throw createError(ErrorCode.API_ERROR, `获取 Bug ${bugId} 详情失败: ${error instanceof Error ? error.message : String(error)}`, undefined, error);
205
+ }
206
+ });
207
+ // Add updateTask tool
208
+ server.tool("updateTask", {
209
+ taskId: z.number(),
210
+ update: z.object({
211
+ consumed: z.number().optional(),
212
+ left: z.number().optional(),
213
+ status: z.enum(['wait', 'doing', 'done']).optional(),
214
+ finishedDate: z.string().optional(),
215
+ comment: z.string().optional()
216
+ })
217
+ }, async ({ taskId, update }) => {
218
+ await ensureInitialized();
219
+ try {
220
+ const task = await zentaoApi.updateTask(taskId, update);
221
+ // 添加操作完成后的建议
222
+ const suggestions = suggestNextActionsForTask(task);
223
+ let resultText = JSON.stringify(task, null, 2);
224
+ if (suggestions.length > 0) {
225
+ const suggestionsMarkdown = formatSuggestionsAsMarkdown(suggestions);
226
+ resultText += `\n\n## ✅ 操作完成\n\n任务已成功更新。\n\n${suggestionsMarkdown}`;
227
+ }
228
+ return {
229
+ content: [{ type: "text", text: resultText }]
230
+ };
231
+ }
232
+ catch (error) {
233
+ if (error instanceof ZentaoError) {
234
+ throw error;
235
+ }
236
+ throw createError(ErrorCode.API_ERROR, `更新任务失败: ${error instanceof Error ? error.message : String(error)}`, undefined, error);
237
+ }
238
+ });
239
+ // Add finishTask tool
240
+ server.tool("finishTask", {
241
+ taskId: z.number(),
242
+ update: z.object({
243
+ consumed: z.number().optional(),
244
+ left: z.number().optional(),
245
+ comment: z.string().optional()
246
+ }).optional()
247
+ }, async ({ taskId, update }) => {
248
+ await ensureInitialized();
249
+ try {
250
+ await zentaoApi.finishTask(taskId, update);
251
+ // 获取更新后的任务详情
252
+ const task = await zentaoApi.getTaskDetail(taskId);
253
+ // 添加操作完成后的建议
254
+ const suggestions = suggestNextActionsForTask(task, true);
255
+ let resultText = JSON.stringify(task, null, 2);
256
+ if (suggestions.length > 0) {
257
+ const suggestionsMarkdown = formatSuggestionsAsMarkdown(suggestions);
258
+ resultText += `\n\n## ✅ 操作完成\n\n任务已成功完成。\n\n${suggestionsMarkdown}`;
259
+ }
260
+ return {
261
+ content: [{ type: "text", text: resultText }]
262
+ };
263
+ }
264
+ catch (error) {
265
+ if (error instanceof ZentaoError) {
266
+ throw error;
267
+ }
268
+ throw createError(ErrorCode.API_ERROR, `完成任务失败: ${error instanceof Error ? error.message : String(error)}`, undefined, error);
269
+ }
270
+ });
271
+ // Add resolveBug tool
272
+ server.tool("resolveBug", {
273
+ bugId: z.number(),
274
+ resolution: z.object({
275
+ resolution: z.enum(['fixed', 'notrepro', 'duplicate', 'bydesign', 'willnotfix', 'tostory', 'external']),
276
+ resolvedBuild: z.string().optional(),
277
+ duplicateBug: z.number().optional(),
278
+ comment: z.string().optional()
279
+ })
280
+ }, async ({ bugId, resolution }) => {
281
+ await ensureInitialized();
282
+ try {
283
+ await zentaoApi.resolveBug(bugId, resolution);
284
+ const bug = await zentaoApi.getBugDetail(bugId);
285
+ // 添加操作完成后的建议
286
+ const relatedStory = await zentaoApi.getBugRelatedStory(bugId).catch(() => null);
287
+ const suggestions = suggestNextActionsForBug(bug, relatedStory !== null, bug.status === 'active');
288
+ let resultText = JSON.stringify(bug, null, 2);
289
+ if (suggestions.length > 0) {
290
+ const suggestionsMarkdown = formatSuggestionsAsMarkdown(suggestions);
291
+ resultText += `\n\n## ✅ 操作完成\n\nBug 已成功解决。\n\n${suggestionsMarkdown}`;
292
+ }
293
+ return {
294
+ content: [{ type: "text", text: resultText }]
295
+ };
296
+ }
297
+ catch (error) {
298
+ if (error instanceof ZentaoError) {
299
+ throw error;
300
+ }
301
+ throw createError(ErrorCode.API_ERROR, `解决 Bug 失败: ${error instanceof Error ? error.message : String(error)}`, undefined, error);
302
+ }
303
+ });
304
+ // Add getProductStories tool
305
+ server.tool("getProductStories", {
306
+ productId: z.number(),
307
+ status: z.enum(['draft', 'active', 'closed', 'changed', 'all']).optional()
308
+ }, async ({ productId, status }) => {
309
+ await ensureInitialized();
310
+ try {
311
+ const stories = await zentaoApi.getProductStories(productId, status);
312
+ return {
313
+ content: [{ type: "text", text: JSON.stringify(stories, null, 2) }]
314
+ };
315
+ }
316
+ catch (error) {
317
+ if (error instanceof ZentaoError) {
318
+ throw error;
319
+ }
320
+ throw createError(ErrorCode.API_ERROR, `获取产品 ${productId} 的需求列表失败: ${error instanceof Error ? error.message : String(error)}`, undefined, error);
321
+ }
322
+ });
323
+ // Add getStoryDetail tool
324
+ server.tool("getStoryDetail", {
325
+ storyId: z.number(),
326
+ downloadImages: z.boolean().optional().default(true)
327
+ }, async ({ storyId, downloadImages: shouldDownloadImages = true }) => {
328
+ await ensureInitialized();
329
+ try {
330
+ const story = await zentaoApi.getStoryDetail(storyId);
331
+ const result = {
332
+ ...story,
333
+ images: [],
334
+ fileIds: [],
335
+ hasImages: false,
336
+ hasFiles: false
337
+ };
338
+ // 提取图片和文件信息
339
+ if (story.spec) {
340
+ result.images = zentaoApi.extractImageUrls(story.spec);
341
+ result.fileIds = zentaoApi.extractFileIds(story.spec);
342
+ result.hasImages = result.images.length > 0;
343
+ result.hasFiles = result.fileIds.length > 0;
344
+ }
345
+ // 下载图片(并行)
346
+ if (shouldDownloadImages && result.images.length > 0) {
347
+ result.downloadedImages = await downloadImages(zentaoApi, result.images, true);
348
+ }
349
+ // 构建返回内容,包含文本和图片
350
+ const content = [
351
+ { type: "text", text: JSON.stringify(result, null, 2) }
352
+ ];
353
+ // 添加图片内容(使用MCP协议的image类型)
354
+ if (result.downloadedImages && result.downloadedImages.length > 0) {
355
+ const imageContent = buildImageContent(result.downloadedImages, 'story', storyId);
356
+ content.push(...imageContent);
357
+ }
358
+ // 添加下一步建议
359
+ const relatedBugs = await zentaoApi.getStoryRelatedBugs(storyId).catch(() => []);
360
+ const testCases = await zentaoApi.getStoryTestCases(storyId).catch(() => []);
361
+ const suggestions = suggestNextActionsForStory(story, relatedBugs.length > 0, testCases.length > 0);
362
+ if (suggestions.length > 0) {
363
+ const suggestionsMarkdown = formatSuggestionsAsMarkdown(suggestions);
364
+ content.push({
365
+ type: "text",
366
+ text: `\n\n${suggestionsMarkdown}`
367
+ });
368
+ }
369
+ return {
370
+ content: content
371
+ };
372
+ }
373
+ catch (error) {
374
+ if (error instanceof ZentaoError) {
375
+ throw error;
376
+ }
377
+ throw createError(ErrorCode.API_ERROR, `获取需求 ${storyId} 详情失败: ${error instanceof Error ? error.message : String(error)}`, undefined, error);
378
+ }
379
+ });
380
+ // Add searchStories tool
381
+ server.tool("searchStories", {
382
+ keyword: z.string(),
383
+ productId: z.number().optional(),
384
+ status: z.enum(['draft', 'active', 'closed', 'changed', 'all']).optional(),
385
+ limit: z.number().optional().default(20)
386
+ }, async ({ keyword, productId, status, limit = 20 }) => {
387
+ await ensureInitialized();
388
+ try {
389
+ const stories = await zentaoApi.searchStories(keyword, {
390
+ productId,
391
+ status: status,
392
+ limit
393
+ });
394
+ return {
395
+ content: [{ type: "text", text: JSON.stringify(stories, null, 2) }]
396
+ };
397
+ }
398
+ catch (error) {
399
+ if (error instanceof ZentaoError) {
400
+ throw error;
401
+ }
402
+ throw createError(ErrorCode.API_ERROR, `搜索需求失败: ${error instanceof Error ? error.message : String(error)}`, undefined, error);
403
+ }
404
+ });
405
+ // Add searchStoriesByProductName tool
406
+ server.tool("searchStoriesByProductName", {
407
+ productName: z.string(),
408
+ keyword: z.string(),
409
+ status: z.enum(['draft', 'active', 'closed', 'changed', 'all']).optional(),
410
+ limit: z.number().optional().default(10)
411
+ }, async ({ productName, keyword, status, limit = 10 }) => {
412
+ await ensureInitialized();
413
+ try {
414
+ const results = await zentaoApi.searchStoriesByProductName(productName, keyword, {
415
+ status: status,
416
+ limit
417
+ });
418
+ return {
419
+ content: [{ type: "text", text: JSON.stringify(results, null, 2) }]
420
+ };
421
+ }
422
+ catch (error) {
423
+ if (error instanceof ZentaoError) {
424
+ throw error;
425
+ }
426
+ throw createError(ErrorCode.API_ERROR, `按产品名称搜索需求失败: ${error instanceof Error ? error.message : String(error)}`, undefined, error);
427
+ }
428
+ });
429
+ // ==================== 测试用例相关接口 ====================
430
+ // Add getProductTestCases tool
431
+ server.tool("getProductTestCases", {
432
+ productId: z.number(),
433
+ status: z.enum(['normal', 'blocked', 'investigate', 'all']).optional(),
434
+ moduleId: z.number().optional()
435
+ }, async ({ productId, status, moduleId }) => {
436
+ await ensureInitialized();
437
+ try {
438
+ const testCases = await zentaoApi.getProductTestCases(productId, status, moduleId);
439
+ return {
440
+ content: [{ type: "text", text: JSON.stringify(testCases, null, 2) }]
441
+ };
442
+ }
443
+ catch (error) {
444
+ if (error instanceof ZentaoError) {
445
+ throw error;
446
+ }
447
+ throw createError(ErrorCode.API_ERROR, `获取产品 ${productId} 的测试用例失败: ${error instanceof Error ? error.message : String(error)}`, undefined, error);
448
+ }
449
+ });
450
+ // Add getTestCaseDetail tool
451
+ server.tool("getTestCaseDetail", {
452
+ caseId: z.number()
453
+ }, async ({ caseId }) => {
454
+ await ensureInitialized();
455
+ try {
456
+ const testCase = await zentaoApi.getTestCaseDetail(caseId);
457
+ return {
458
+ content: [{ type: "text", text: JSON.stringify(testCase, null, 2) }]
459
+ };
460
+ }
461
+ catch (error) {
462
+ if (error instanceof ZentaoError) {
463
+ throw error;
464
+ }
465
+ throw createError(ErrorCode.API_ERROR, `获取测试用例 ${caseId} 详情失败: ${error instanceof Error ? error.message : String(error)}`, undefined, error);
466
+ }
467
+ });
468
+ // Add createTestCase tool
469
+ server.tool("createTestCase", {
470
+ testCase: z.object({
471
+ product: z.number(),
472
+ module: z.number().optional(),
473
+ story: z.number().optional(),
474
+ title: z.string(),
475
+ type: z.string().optional(),
476
+ pri: z.number().optional(),
477
+ precondition: z.string().optional(),
478
+ steps: z.string().optional(),
479
+ status: z.string().optional()
480
+ })
481
+ }, async ({ testCase }) => {
482
+ await ensureInitialized();
483
+ try {
484
+ const result = await zentaoApi.createTestCase(testCase);
485
+ return {
486
+ content: [{ type: "text", text: JSON.stringify({ id: result, success: true }, null, 2) }]
487
+ };
488
+ }
489
+ catch (error) {
490
+ if (error instanceof ZentaoError) {
491
+ throw error;
492
+ }
493
+ throw createError(ErrorCode.API_ERROR, `创建测试用例失败: ${error instanceof Error ? error.message : String(error)}`, undefined, error);
494
+ }
495
+ });
496
+ // Add getStoryTestCases tool
497
+ server.tool("getStoryTestCases", {
498
+ storyId: z.number()
499
+ }, async ({ storyId }) => {
500
+ await ensureInitialized();
501
+ try {
502
+ const testCases = await zentaoApi.getStoryTestCases(storyId);
503
+ return {
504
+ content: [{ type: "text", text: JSON.stringify(testCases, null, 2) }]
505
+ };
506
+ }
507
+ catch (error) {
508
+ if (error instanceof ZentaoError) {
509
+ throw error;
510
+ }
511
+ throw createError(ErrorCode.API_ERROR, `获取需求 ${storyId} 的测试用例失败: ${error instanceof Error ? error.message : String(error)}`, undefined, error);
512
+ }
513
+ });
514
+ // Add getTestTasks tool
515
+ server.tool("getTestTasks", {
516
+ productId: z.number().optional()
517
+ }, async ({ productId }) => {
518
+ await ensureInitialized();
519
+ try {
520
+ const testTasks = await zentaoApi.getTestTasks(productId);
521
+ return {
522
+ content: [{ type: "text", text: JSON.stringify(testTasks, null, 2) }]
523
+ };
524
+ }
525
+ catch (error) {
526
+ if (error instanceof ZentaoError) {
527
+ throw error;
528
+ }
529
+ throw createError(ErrorCode.API_ERROR, `获取测试单列表失败: ${error instanceof Error ? error.message : String(error)}`, undefined, error);
530
+ }
531
+ });
532
+ // Add getTestTaskDetail tool
533
+ server.tool("getTestTaskDetail", {
534
+ taskId: z.number()
535
+ }, async ({ taskId }) => {
536
+ await ensureInitialized();
537
+ try {
538
+ const testTask = await zentaoApi.getTestTaskDetail(taskId);
539
+ return {
540
+ content: [{ type: "text", text: JSON.stringify(testTask, null, 2) }]
541
+ };
542
+ }
543
+ catch (error) {
544
+ if (error instanceof ZentaoError) {
545
+ throw error;
546
+ }
547
+ throw createError(ErrorCode.API_ERROR, `获取测试单 ${taskId} 详情失败: ${error instanceof Error ? error.message : String(error)}`, undefined, error);
548
+ }
549
+ });
550
+ // Add getTestTaskResults tool
551
+ server.tool("getTestTaskResults", {
552
+ taskId: z.number()
553
+ }, async ({ taskId }) => {
554
+ await ensureInitialized();
555
+ try {
556
+ const results = await zentaoApi.getTestTaskResults(taskId);
557
+ return {
558
+ content: [{ type: "text", text: JSON.stringify(results, null, 2) }]
559
+ };
560
+ }
561
+ catch (error) {
562
+ if (error instanceof ZentaoError) {
563
+ throw error;
564
+ }
565
+ throw createError(ErrorCode.API_ERROR, `获取测试单 ${taskId} 的测试结果失败: ${error instanceof Error ? error.message : String(error)}`, undefined, error);
566
+ }
567
+ });
568
+ // Add runTestCase tool
569
+ server.tool("runTestCase", {
570
+ taskId: z.number(),
571
+ testRun: z.object({
572
+ caseId: z.number(),
573
+ version: z.number().optional(),
574
+ result: z.enum(['pass', 'fail', 'blocked', 'skipped']),
575
+ steps: z.string().optional(),
576
+ comment: z.string().optional()
577
+ })
578
+ }, async ({ taskId, testRun }) => {
579
+ await ensureInitialized();
580
+ try {
581
+ await zentaoApi.runTestCase(taskId, testRun);
582
+ return {
583
+ content: [{ type: "text", text: JSON.stringify({ success: true }, null, 2) }]
584
+ };
585
+ }
586
+ catch (error) {
587
+ if (error instanceof ZentaoError) {
588
+ throw error;
589
+ }
590
+ throw createError(ErrorCode.API_ERROR, `执行测试用例失败: ${error instanceof Error ? error.message : String(error)}`, undefined, error);
591
+ }
592
+ });
593
+ // ==================== 关联关系查询 ====================
594
+ // Add getStoryRelatedBugs tool
595
+ server.tool("getStoryRelatedBugs", {
596
+ storyId: z.number()
597
+ }, async ({ storyId }) => {
598
+ await ensureInitialized();
599
+ try {
600
+ const bugs = await zentaoApi.getStoryRelatedBugs(storyId);
601
+ return {
602
+ content: [{ type: "text", text: JSON.stringify(bugs, null, 2) }]
603
+ };
604
+ }
605
+ catch (error) {
606
+ if (error instanceof ZentaoError) {
607
+ throw error;
608
+ }
609
+ throw createError(ErrorCode.API_ERROR, `获取需求 ${storyId} 关联的 Bug 失败: ${error instanceof Error ? error.message : String(error)}`, undefined, error);
610
+ }
611
+ });
612
+ // Add getBugRelatedStory tool
613
+ server.tool("getBugRelatedStory", {
614
+ bugId: z.number()
615
+ }, async ({ bugId }) => {
616
+ await ensureInitialized();
617
+ try {
618
+ const story = await zentaoApi.getBugRelatedStory(bugId);
619
+ if (!story) {
620
+ return {
621
+ content: [{ type: "text", text: JSON.stringify({ message: `Bug ${bugId} 没有关联的需求` }, null, 2) }]
622
+ };
623
+ }
624
+ return {
625
+ content: [{ type: "text", text: JSON.stringify(story, null, 2) }]
626
+ };
627
+ }
628
+ catch (error) {
629
+ if (error instanceof ZentaoError) {
630
+ throw error;
631
+ }
632
+ throw createError(ErrorCode.API_ERROR, `获取 Bug ${bugId} 关联的需求失败: ${error instanceof Error ? error.message : String(error)}`, undefined, error);
633
+ }
634
+ });
635
+ // ==================== 批量操作 ====================
636
+ // Add batchUpdateTasks tool
637
+ server.tool("batchUpdateTasks", {
638
+ taskIds: z.array(z.number()),
639
+ update: z.object({
640
+ consumed: z.number().optional(),
641
+ left: z.number().optional(),
642
+ status: z.enum(['wait', 'doing', 'done']).optional(),
643
+ finishedDate: z.string().optional(),
644
+ comment: z.string().optional()
645
+ })
646
+ }, async ({ taskIds, update }) => {
647
+ await ensureInitialized();
648
+ try {
649
+ const results = [];
650
+ for (const taskId of taskIds) {
651
+ try {
652
+ const task = await zentaoApi.updateTask(taskId, update);
653
+ results.push({ taskId, success: true, task });
654
+ }
655
+ catch (error) {
656
+ results.push({
657
+ taskId,
658
+ success: false,
659
+ error: error instanceof Error ? error.message : String(error)
660
+ });
661
+ }
662
+ }
663
+ return {
664
+ content: [{ type: "text", text: JSON.stringify({ results, total: taskIds.length, success: results.filter(r => r.success).length }, null, 2) }]
665
+ };
666
+ }
667
+ catch (error) {
668
+ if (error instanceof ZentaoError) {
669
+ throw error;
670
+ }
671
+ throw createError(ErrorCode.API_ERROR, `批量更新任务失败: ${error instanceof Error ? error.message : String(error)}`, undefined, error);
672
+ }
673
+ });
674
+ // Add batchResolveBugs tool
675
+ server.tool("batchResolveBugs", {
676
+ bugIds: z.array(z.number()),
677
+ resolution: z.object({
678
+ resolution: z.enum(['fixed', 'notrepro', 'duplicate', 'bydesign', 'willnotfix', 'tostory', 'external']),
679
+ resolvedBuild: z.string().optional(),
680
+ duplicateBug: z.number().optional(),
681
+ comment: z.string().optional()
682
+ })
683
+ }, async ({ bugIds, resolution }) => {
684
+ await ensureInitialized();
685
+ try {
686
+ const results = [];
687
+ for (const bugId of bugIds) {
688
+ try {
689
+ await zentaoApi.resolveBug(bugId, resolution);
690
+ const bug = await zentaoApi.getBugDetail(bugId);
691
+ results.push({ bugId, success: true, bug });
692
+ }
693
+ catch (error) {
694
+ results.push({
695
+ bugId,
696
+ success: false,
697
+ error: error instanceof Error ? error.message : String(error)
698
+ });
699
+ }
700
+ }
701
+ return {
702
+ content: [{ type: "text", text: JSON.stringify({ results, total: bugIds.length, success: results.filter(r => r.success).length }, null, 2) }]
703
+ };
704
+ }
705
+ catch (error) {
706
+ if (error instanceof ZentaoError) {
707
+ throw error;
708
+ }
709
+ throw createError(ErrorCode.API_ERROR, `批量解决 Bug 失败: ${error instanceof Error ? error.message : String(error)}`, undefined, error);
710
+ }
711
+ });
712
+ // ==================== 数据统计 ====================
713
+ // Add getMyTaskStatistics tool
714
+ server.tool("getMyTaskStatistics", {}, async () => {
715
+ await ensureInitialized();
716
+ try {
717
+ const tasks = await zentaoApi.getMyTasks();
718
+ const statistics = {
719
+ total: tasks.length,
720
+ wait: tasks.filter(t => t.status === 'wait').length,
721
+ doing: tasks.filter(t => t.status === 'doing').length,
722
+ done: tasks.filter(t => t.status === 'done').length,
723
+ byPriority: {
724
+ '1': tasks.filter(t => t.pri === 1).length,
725
+ '2': tasks.filter(t => t.pri === 2).length,
726
+ '3': tasks.filter(t => t.pri === 3).length,
727
+ '4': tasks.filter(t => t.pri === 4).length,
728
+ }
729
+ };
730
+ return {
731
+ content: [{ type: "text", text: JSON.stringify(statistics, null, 2) }]
732
+ };
733
+ }
734
+ catch (error) {
735
+ if (error instanceof ZentaoError) {
736
+ throw error;
737
+ }
738
+ throw createError(ErrorCode.API_ERROR, `获取任务统计失败: ${error instanceof Error ? error.message : String(error)}`, undefined, error);
739
+ }
740
+ });
741
+ // Add getMyBugStatistics tool
742
+ server.tool("getMyBugStatistics", {}, async () => {
743
+ await ensureInitialized();
744
+ try {
745
+ const bugs = await zentaoApi.getMyBugs();
746
+ const statistics = {
747
+ total: bugs.length,
748
+ active: bugs.filter(b => b.status === 'active').length,
749
+ resolved: bugs.filter(b => b.status === 'resolved').length,
750
+ closed: bugs.filter(b => b.status === 'closed').length,
751
+ bySeverity: {
752
+ '1': bugs.filter(b => b.severity === 1).length,
753
+ '2': bugs.filter(b => b.severity === 2).length,
754
+ '3': bugs.filter(b => b.severity === 3).length,
755
+ '4': bugs.filter(b => b.severity === 4).length,
756
+ }
757
+ };
758
+ return {
759
+ content: [{ type: "text", text: JSON.stringify(statistics, null, 2) }]
760
+ };
761
+ }
762
+ catch (error) {
763
+ if (error instanceof ZentaoError) {
764
+ throw error;
765
+ }
766
+ throw createError(ErrorCode.API_ERROR, `获取 Bug 统计失败: ${error instanceof Error ? error.message : String(error)}`, undefined, error);
767
+ }
768
+ });
769
+ // ==================== AI 编程辅助功能 ====================
770
+ // Add getDevelopmentContext tool - 获取完整开发上下文
771
+ server.tool("getDevelopmentContext", {
772
+ entityType: z.enum(['story', 'bug']),
773
+ entityId: z.number(),
774
+ format: z.enum(['json', 'markdown']).optional().default('json')
775
+ }, async ({ entityType, entityId, format = 'json' }) => {
776
+ await ensureInitialized();
777
+ try {
778
+ if (entityType === 'story') {
779
+ // 获取需求完整上下文
780
+ const story = await zentaoApi.getStoryDetail(entityId);
781
+ const relatedBugs = await zentaoApi.getStoryRelatedBugs(entityId);
782
+ const testCases = await zentaoApi.getStoryTestCases(entityId);
783
+ const context = {
784
+ story,
785
+ relatedBugs,
786
+ testCases,
787
+ summary: {
788
+ bugsCount: relatedBugs.length,
789
+ testCasesCount: testCases.length
790
+ }
791
+ };
792
+ if (format === 'markdown') {
793
+ let markdown = formatStoryAsMarkdown(story);
794
+ markdown += '\n\n## 关联信息\n\n';
795
+ markdown += `- **关联 Bug**: ${relatedBugs.length} 个\n`;
796
+ markdown += `- **测试用例**: ${testCases.length} 个\n\n`;
797
+ if (relatedBugs.length > 0) {
798
+ markdown += '### 关联的 Bug\n\n';
799
+ relatedBugs.forEach(bug => {
800
+ markdown += `- ${generateBugSummary(bug)}\n`;
801
+ });
802
+ markdown += '\n';
803
+ }
804
+ // 添加下一步建议
805
+ const suggestions = suggestNextActionsForStory(story, relatedBugs.length > 0, testCases.length > 0);
806
+ if (suggestions.length > 0) {
807
+ const suggestionsMarkdown = formatSuggestionsAsMarkdown(suggestions);
808
+ markdown += `\n\n${suggestionsMarkdown}`;
809
+ }
810
+ return {
811
+ content: [{ type: "text", text: markdown }]
812
+ };
813
+ }
814
+ // JSON 格式也添加建议
815
+ const suggestions = suggestNextActionsForStory(story, relatedBugs.length > 0, testCases.length > 0);
816
+ context.suggestions = suggestions;
817
+ return {
818
+ content: [{ type: "text", text: JSON.stringify(context, null, 2) }]
819
+ };
820
+ }
821
+ else {
822
+ // 获取 Bug 完整上下文
823
+ const bug = await zentaoApi.getBugDetail(entityId);
824
+ const relatedStory = await zentaoApi.getBugRelatedStory(entityId);
825
+ const context = {
826
+ bug,
827
+ relatedStory: relatedStory || null,
828
+ summary: {
829
+ hasRelatedStory: relatedStory !== null
830
+ }
831
+ };
832
+ if (format === 'markdown') {
833
+ let markdown = formatBugAsMarkdown(bug);
834
+ markdown += '\n\n## 关联信息\n\n';
835
+ if (relatedStory) {
836
+ markdown += '### 关联的需求\n\n';
837
+ markdown += formatStoryAsMarkdown(relatedStory);
838
+ markdown += '\n';
839
+ }
840
+ else {
841
+ markdown += '- **关联需求**: 无\n\n';
842
+ }
843
+ // 添加下一步建议
844
+ const suggestions = suggestNextActionsForBug(bug, relatedStory !== null, bug.status === 'active');
845
+ if (suggestions.length > 0) {
846
+ const suggestionsMarkdown = formatSuggestionsAsMarkdown(suggestions);
847
+ markdown += `\n\n${suggestionsMarkdown}`;
848
+ }
849
+ return {
850
+ content: [{ type: "text", text: markdown }]
851
+ };
852
+ }
853
+ // JSON 格式也添加建议
854
+ const suggestions = suggestNextActionsForBug(bug, relatedStory !== null, bug.status === 'active');
855
+ context.suggestions = suggestions;
856
+ return {
857
+ content: [{ type: "text", text: JSON.stringify(context, null, 2) }]
858
+ };
859
+ }
860
+ }
861
+ catch (error) {
862
+ if (error instanceof ZentaoError) {
863
+ throw error;
864
+ }
865
+ throw createError(ErrorCode.API_ERROR, `获取${entityType === 'story' ? '需求' : 'Bug'} ${entityId} 的完整上下文失败: ${error instanceof Error ? error.message : String(error)}`, undefined, error);
866
+ }
867
+ });
868
+ // Add generateStorySummary tool - 生成需求摘要
869
+ server.tool("generateStorySummary", {
870
+ storyId: z.number(),
871
+ format: z.enum(['json', 'markdown', 'text']).optional().default('text')
872
+ }, async ({ storyId, format = 'text' }) => {
873
+ await ensureInitialized();
874
+ try {
875
+ const story = await zentaoApi.getStoryDetail(storyId);
876
+ if (format === 'markdown') {
877
+ return {
878
+ content: [{ type: "text", text: formatStoryAsMarkdown(story) }]
879
+ };
880
+ }
881
+ else if (format === 'json') {
882
+ return {
883
+ content: [{ type: "text", text: JSON.stringify(story, null, 2) }]
884
+ };
885
+ }
886
+ else {
887
+ return {
888
+ content: [{ type: "text", text: generateStorySummary(story) }]
889
+ };
890
+ }
891
+ }
892
+ catch (error) {
893
+ if (error instanceof ZentaoError) {
894
+ throw error;
895
+ }
896
+ throw createError(ErrorCode.API_ERROR, `生成需求 ${storyId} 摘要失败: ${error instanceof Error ? error.message : String(error)}`, undefined, error);
897
+ }
898
+ });
899
+ // Add generateBugSummary tool - 生成 Bug 摘要
900
+ server.tool("generateBugSummary", {
901
+ bugId: z.number(),
902
+ format: z.enum(['json', 'markdown', 'text']).optional().default('text')
903
+ }, async ({ bugId, format = 'text' }) => {
904
+ await ensureInitialized();
905
+ try {
906
+ const bug = await zentaoApi.getBugDetail(bugId);
907
+ if (format === 'markdown') {
908
+ return {
909
+ content: [{ type: "text", text: formatBugAsMarkdown(bug) }]
910
+ };
911
+ }
912
+ else if (format === 'json') {
913
+ return {
914
+ content: [{ type: "text", text: JSON.stringify(bug, null, 2) }]
915
+ };
916
+ }
917
+ else {
918
+ return {
919
+ content: [{ type: "text", text: generateBugSummary(bug) }]
920
+ };
921
+ }
922
+ }
923
+ catch (error) {
924
+ if (error instanceof ZentaoError) {
925
+ throw error;
926
+ }
927
+ throw createError(ErrorCode.API_ERROR, `生成 Bug ${bugId} 摘要失败: ${error instanceof Error ? error.message : String(error)}`, undefined, error);
928
+ }
929
+ });
930
+ // Add formatTaskAsMarkdown tool - 格式化任务为 Markdown
931
+ server.tool("formatTaskAsMarkdown", {
932
+ taskId: z.number()
933
+ }, async ({ taskId }) => {
934
+ await ensureInitialized();
935
+ try {
936
+ const task = await zentaoApi.getTaskDetail(taskId);
937
+ return {
938
+ content: [{ type: "text", text: formatTaskAsMarkdown(task) }]
939
+ };
940
+ }
941
+ catch (error) {
942
+ if (error instanceof ZentaoError) {
943
+ throw error;
944
+ }
945
+ throw createError(ErrorCode.API_ERROR, `格式化任务 ${taskId} 失败: ${error instanceof Error ? error.message : String(error)}`, undefined, error);
946
+ }
947
+ });
948
+ // ==================== 智能分析功能 ====================
949
+ // Add analyzeStoryComplexity tool
950
+ server.tool("analyzeStoryComplexity", {
951
+ storyId: z.number()
952
+ }, async ({ storyId }) => {
953
+ await ensureInitialized();
954
+ try {
955
+ const story = await zentaoApi.getStoryDetail(storyId);
956
+ const relatedBugs = await zentaoApi.getStoryRelatedBugs(storyId);
957
+ const testCases = await zentaoApi.getStoryTestCases(storyId);
958
+ const analysis = analyzeStoryComplexity(story, relatedBugs.length, testCases.length);
959
+ return {
960
+ content: [{ type: "text", text: JSON.stringify({
961
+ storyId,
962
+ storyTitle: story.title,
963
+ analysis
964
+ }, null, 2) }]
965
+ };
966
+ }
967
+ catch (error) {
968
+ if (error instanceof ZentaoError) {
969
+ throw error;
970
+ }
971
+ throw createError(ErrorCode.API_ERROR, `分析需求 ${storyId} 复杂度失败: ${error instanceof Error ? error.message : String(error)}`, undefined, error);
972
+ }
973
+ });
974
+ // Add analyzeBugPriority tool
975
+ server.tool("analyzeBugPriority", {
976
+ bugId: z.number()
977
+ }, async ({ bugId }) => {
978
+ await ensureInitialized();
979
+ try {
980
+ const bug = await zentaoApi.getBugDetail(bugId);
981
+ const relatedStory = await zentaoApi.getBugRelatedStory(bugId);
982
+ const analysis = analyzeBugPriority(bug, relatedStory !== null);
983
+ return {
984
+ content: [{ type: "text", text: JSON.stringify({
985
+ bugId,
986
+ bugTitle: bug.title,
987
+ analysis
988
+ }, null, 2) }]
989
+ };
990
+ }
991
+ catch (error) {
992
+ if (error instanceof ZentaoError) {
993
+ throw error;
994
+ }
995
+ throw createError(ErrorCode.API_ERROR, `分析 Bug ${bugId} 优先级失败: ${error instanceof Error ? error.message : String(error)}`, undefined, error);
996
+ }
997
+ });
998
+ // Add analyzeTaskWorkload tool
999
+ server.tool("analyzeTaskWorkload", {
1000
+ taskId: z.number()
1001
+ }, async ({ taskId }) => {
1002
+ await ensureInitialized();
1003
+ try {
1004
+ const task = await zentaoApi.getTaskDetail(taskId);
1005
+ const analysis = analyzeTaskWorkload(task);
1006
+ return {
1007
+ content: [{ type: "text", text: JSON.stringify({
1008
+ taskId,
1009
+ taskName: task.name,
1010
+ analysis
1011
+ }, null, 2) }]
1012
+ };
1013
+ }
1014
+ catch (error) {
1015
+ if (error instanceof ZentaoError) {
1016
+ throw error;
1017
+ }
1018
+ throw createError(ErrorCode.API_ERROR, `分析任务 ${taskId} 工作量失败: ${error instanceof Error ? error.message : String(error)}`, undefined, error);
1019
+ }
1020
+ });
1021
+ // ==================== 代码生成提示工具 ====================
1022
+ // Add generateCodePromptFromStory tool - 根据需求生成代码框架提示
1023
+ server.tool("generateCodePromptFromStory", {
1024
+ storyId: z.number()
1025
+ }, async ({ storyId }) => {
1026
+ await ensureInitialized();
1027
+ try {
1028
+ const story = await zentaoApi.getStoryDetail(storyId);
1029
+ const relatedBugs = await zentaoApi.getStoryRelatedBugs(storyId);
1030
+ const testCases = await zentaoApi.getStoryTestCases(storyId);
1031
+ const prompt = `# 根据需求生成代码框架
1032
+
1033
+ ## 需求信息
1034
+ - **需求 ID**: ${story.id}
1035
+ - **需求标题**: ${story.title}
1036
+ - **需求状态**: ${story.status}
1037
+ - **优先级**: ${story.pri}
1038
+ - **产品**: ${story.productName || '未知'}
1039
+
1040
+ ## 需求描述
1041
+ ${story.spec || '暂无描述'}
1042
+
1043
+ ## 关联信息
1044
+ - **关联 Bug 数量**: ${relatedBugs.length}
1045
+ - **测试用例数量**: ${testCases.length}
1046
+
1047
+ ## 任务
1048
+ 请根据以上需求信息,生成代码框架,包括:
1049
+ 1. 函数/类的基本结构
1050
+ 2. 必要的注释和文档
1051
+ 3. 输入输出参数定义
1052
+ 4. 错误处理逻辑
1053
+ 5. 基本的测试用例框架
1054
+
1055
+ 请使用清晰的代码结构,并添加必要的注释说明。`;
1056
+ return {
1057
+ content: [{ type: "text", text: prompt }]
1058
+ };
1059
+ }
1060
+ catch (error) {
1061
+ if (error instanceof ZentaoError) {
1062
+ throw error;
1063
+ }
1064
+ throw createError(ErrorCode.API_ERROR, `生成代码提示失败: ${error instanceof Error ? error.message : String(error)}`, undefined, error);
1065
+ }
1066
+ });
1067
+ // Add generateTestPromptFromBug tool - 根据 Bug 生成测试用例提示
1068
+ server.tool("generateTestPromptFromBug", {
1069
+ bugId: z.number()
1070
+ }, async ({ bugId }) => {
1071
+ await ensureInitialized();
1072
+ try {
1073
+ const bug = await zentaoApi.getBugDetail(bugId);
1074
+ const relatedStory = await zentaoApi.getBugRelatedStory(bugId);
1075
+ const prompt = `# 根据 Bug 生成测试用例
1076
+
1077
+ ## Bug 信息
1078
+ - **Bug ID**: ${bug.id}
1079
+ - **Bug 标题**: ${bug.title}
1080
+ - **Bug 状态**: ${bug.status}
1081
+ - **严重程度**: ${bug.severity}
1082
+ - **产品**: ${bug.productName || '未知'}
1083
+
1084
+ ## 复现步骤
1085
+ ${bug.steps || '暂无复现步骤'}
1086
+
1087
+ ${relatedStory ? `## 关联需求
1088
+ - **需求 ID**: ${relatedStory.id}
1089
+ - **需求标题**: ${relatedStory.title}
1090
+ ` : ''}
1091
+
1092
+ ## 任务
1093
+ 请根据以上 Bug 信息,生成测试用例,包括:
1094
+ 1. 测试用例标题
1095
+ 2. 前置条件
1096
+ 3. 测试步骤(基于复现步骤)
1097
+ 4. 预期结果
1098
+ 5. 测试数据准备
1099
+
1100
+ 请确保测试用例能够覆盖 Bug 的复现场景。`;
1101
+ return {
1102
+ content: [{ type: "text", text: prompt }]
1103
+ };
1104
+ }
1105
+ catch (error) {
1106
+ if (error instanceof ZentaoError) {
1107
+ throw error;
1108
+ }
1109
+ throw createError(ErrorCode.API_ERROR, `生成测试提示失败: ${error instanceof Error ? error.message : String(error)}`, undefined, error);
1110
+ }
1111
+ });
1112
+ // Add generateCodeReviewChecklist tool - 生成代码审查检查清单
1113
+ server.tool("generateCodeReviewChecklist", {
1114
+ entityType: z.enum(['story', 'bug']),
1115
+ entityId: z.number()
1116
+ }, async ({ entityType, entityId }) => {
1117
+ await ensureInitialized();
1118
+ try {
1119
+ let entityInfo = '';
1120
+ let context = '';
1121
+ if (entityType === 'story') {
1122
+ const story = await zentaoApi.getStoryDetail(entityId);
1123
+ const relatedBugs = await zentaoApi.getStoryRelatedBugs(entityId);
1124
+ entityInfo = `需求 #${story.id}: ${story.title}\n需求描述: ${story.spec || '暂无'}`;
1125
+ context = `关联 Bug: ${relatedBugs.length} 个`;
1126
+ }
1127
+ else {
1128
+ const bug = await zentaoApi.getBugDetail(entityId);
1129
+ const relatedStory = await zentaoApi.getBugRelatedStory(entityId);
1130
+ entityInfo = `Bug #${bug.id}: ${bug.title}\n复现步骤: ${bug.steps || '暂无'}`;
1131
+ context = relatedStory ? `关联需求: #${relatedStory.id} - ${relatedStory.title}` : '无关联需求';
1132
+ }
1133
+ const prompt = `# 代码审查检查清单
1134
+
1135
+ ## ${entityType === 'story' ? '需求' : 'Bug'} 信息
1136
+ ${entityInfo}
1137
+
1138
+ ## 关联信息
1139
+ ${context}
1140
+
1141
+ ## 代码审查检查清单
1142
+
1143
+ 请根据以上${entityType === 'story' ? '需求' : 'Bug'}信息,检查代码是否符合以下要求:
1144
+
1145
+ ### 功能完整性
1146
+ - [ ] 代码是否实现了所有需求点?
1147
+ - [ ] 边界情况是否处理?
1148
+ - [ ] 错误处理是否完善?
1149
+
1150
+ ### 代码质量
1151
+ - [ ] 代码结构是否清晰?
1152
+ - [ ] 命名是否规范?
1153
+ - [ ] 注释是否充分?
1154
+ - [ ] 是否有重复代码?
1155
+
1156
+ ### 测试覆盖
1157
+ - [ ] 是否有单元测试?
1158
+ - [ ] 测试用例是否覆盖主要场景?
1159
+ - [ ] 边界情况是否有测试?
1160
+
1161
+ ### 性能和安全
1162
+ - [ ] 性能是否满足要求?
1163
+ - [ ] 是否有安全风险?
1164
+ - [ ] 资源是否正确释放?
1165
+
1166
+ 请逐项检查,并提供具体的改进建议。`;
1167
+ return {
1168
+ content: [{ type: "text", text: prompt }]
1169
+ };
1170
+ }
1171
+ catch (error) {
1172
+ if (error instanceof ZentaoError) {
1173
+ throw error;
1174
+ }
1175
+ throw createError(ErrorCode.API_ERROR, `生成代码审查检查清单失败: ${error instanceof Error ? error.message : String(error)}`, undefined, error);
1176
+ }
1177
+ });
1178
+ // ==================== 获取操作建议工具 ====================
1179
+ // Add getNextActionSuggestions tool - 获取下一步操作建议
1180
+ server.tool("getNextActionSuggestions", {
1181
+ entityType: z.enum(['story', 'bug', 'task']),
1182
+ entityId: z.number()
1183
+ }, async ({ entityType, entityId }) => {
1184
+ await ensureInitialized();
1185
+ try {
1186
+ let suggestions = [];
1187
+ if (entityType === 'story') {
1188
+ const story = await zentaoApi.getStoryDetail(entityId);
1189
+ const relatedBugs = await zentaoApi.getStoryRelatedBugs(entityId).catch(() => []);
1190
+ const testCases = await zentaoApi.getStoryTestCases(entityId).catch(() => []);
1191
+ suggestions = suggestNextActionsForStory(story, relatedBugs.length > 0, testCases.length > 0);
1192
+ }
1193
+ else if (entityType === 'bug') {
1194
+ const bug = await zentaoApi.getBugDetail(entityId);
1195
+ const relatedStory = await zentaoApi.getBugRelatedStory(entityId);
1196
+ suggestions = suggestNextActionsForBug(bug, relatedStory !== null, bug.status === 'active');
1197
+ }
1198
+ else {
1199
+ const task = await zentaoApi.getTaskDetail(entityId);
1200
+ suggestions = suggestNextActionsForTask(task);
1201
+ }
1202
+ const markdown = formatSuggestionsAsMarkdown(suggestions);
1203
+ return {
1204
+ content: [
1205
+ { type: "text", text: JSON.stringify({ suggestions }, null, 2) },
1206
+ { type: "text", text: `\n\n${markdown}` }
1207
+ ]
1208
+ };
1209
+ }
1210
+ catch (error) {
1211
+ if (error instanceof ZentaoError) {
1212
+ throw error;
1213
+ }
1214
+ throw createError(ErrorCode.API_ERROR, `获取操作建议失败: ${error instanceof Error ? error.message : String(error)}`, undefined, error);
1215
+ }
1216
+ });
1217
+ // ==================== 根据需求/Bug创建任务 ====================
1218
+ // Add createTaskFromStory tool - 根据需求创建任务
1219
+ server.tool("createTaskFromStory", {
1220
+ storyId: z.number(),
1221
+ taskName: z.string(),
1222
+ estimate: z.number().optional(),
1223
+ assignedTo: z.string().optional(),
1224
+ desc: z.string().optional()
1225
+ }, async ({ storyId, taskName, estimate, assignedTo, desc }) => {
1226
+ await ensureInitialized();
1227
+ try {
1228
+ // 获取需求详情
1229
+ const story = await zentaoApi.getStoryDetail(storyId);
1230
+ // 注意:禅道11.x Legacy API可能不支持直接通过API创建任务
1231
+ // 这里提供一个建议和需求信息的组合
1232
+ const suggestion = {
1233
+ message: "禅道11.x Legacy API可能不支持直接通过API创建任务",
1234
+ suggestion: `请通过禅道Web界面为需求 #${storyId} 创建任务`,
1235
+ storyInfo: {
1236
+ id: story.id,
1237
+ title: story.title,
1238
+ product: story.productName,
1239
+ status: story.status
1240
+ },
1241
+ taskInfo: {
1242
+ name: taskName,
1243
+ estimate: estimate,
1244
+ assignedTo: assignedTo,
1245
+ desc: desc || `基于需求 #${storyId}: ${story.title}`
1246
+ },
1247
+ manualSteps: [
1248
+ `1. 访问禅道Web界面`,
1249
+ `2. 打开需求 #${storyId}: ${story.title}`,
1250
+ `3. 在需求详情页创建任务`,
1251
+ `4. 任务名称: ${taskName}`,
1252
+ estimate ? `5. 预估工时: ${estimate} 小时` : '',
1253
+ assignedTo ? `6. 指派给: ${assignedTo}` : ''
1254
+ ].filter(Boolean)
1255
+ };
1256
+ return {
1257
+ content: [{
1258
+ type: "text",
1259
+ text: JSON.stringify({
1260
+ success: false,
1261
+ reason: "API不支持",
1262
+ ...suggestion
1263
+ }, null, 2)
1264
+ }]
1265
+ };
1266
+ }
1267
+ catch (error) {
1268
+ if (error instanceof ZentaoError) {
1269
+ throw error;
1270
+ }
1271
+ throw createError(ErrorCode.API_ERROR, `根据需求创建任务失败: ${error instanceof Error ? error.message : String(error)}`, undefined, error);
1272
+ }
1273
+ });
1274
+ // Add createTaskFromBug tool - 根据Bug创建修复任务
1275
+ server.tool("createTaskFromBug", {
1276
+ bugId: z.number(),
1277
+ taskName: z.string().optional(),
1278
+ estimate: z.number().optional(),
1279
+ assignedTo: z.string().optional(),
1280
+ desc: z.string().optional()
1281
+ }, async ({ bugId, taskName, estimate, assignedTo, desc }) => {
1282
+ await ensureInitialized();
1283
+ try {
1284
+ // 获取Bug详情
1285
+ const bug = await zentaoApi.getBugDetail(bugId);
1286
+ const relatedStory = await zentaoApi.getBugRelatedStory(bugId);
1287
+ const defaultTaskName = taskName || `修复Bug #${bugId}: ${bug.title}`;
1288
+ // 注意:禅道11.x Legacy API可能不支持直接通过API创建任务
1289
+ const suggestion = {
1290
+ message: "禅道11.x Legacy API可能不支持直接通过API创建任务",
1291
+ suggestion: `请通过禅道Web界面为Bug #${bugId} 创建修复任务`,
1292
+ bugInfo: {
1293
+ id: bug.id,
1294
+ title: bug.title,
1295
+ status: bug.status,
1296
+ severity: bug.severity,
1297
+ product: bug.productName
1298
+ },
1299
+ relatedStory: relatedStory ? {
1300
+ id: relatedStory.id,
1301
+ title: relatedStory.title
1302
+ } : null,
1303
+ taskInfo: {
1304
+ name: defaultTaskName,
1305
+ estimate: estimate,
1306
+ assignedTo: assignedTo,
1307
+ desc: desc || `修复Bug #${bugId}: ${bug.title}\n\n复现步骤:\n${bug.steps || '无'}`
1308
+ },
1309
+ manualSteps: [
1310
+ `1. 访问禅道Web界面`,
1311
+ `2. 打开Bug #${bugId}: ${bug.title}`,
1312
+ `3. 在Bug详情页创建修复任务`,
1313
+ `4. 任务名称: ${defaultTaskName}`,
1314
+ estimate ? `5. 预估工时: ${estimate} 小时` : '',
1315
+ assignedTo ? `6. 指派给: ${assignedTo}` : ''
1316
+ ].filter(Boolean)
1317
+ };
1318
+ return {
1319
+ content: [{
1320
+ type: "text",
1321
+ text: JSON.stringify({
1322
+ success: false,
1323
+ reason: "API不支持",
1324
+ ...suggestion
1325
+ }, null, 2)
1326
+ }]
1327
+ };
1328
+ }
1329
+ catch (error) {
1330
+ if (error instanceof ZentaoError) {
1331
+ throw error;
1332
+ }
1333
+ throw createError(ErrorCode.API_ERROR, `根据Bug创建任务失败: ${error instanceof Error ? error.message : String(error)}`, undefined, error);
1334
+ }
1335
+ });
1336
+ // Start receiving messages on stdin and sending messages on stdout
1337
+ const transport = new StdioServerTransport();
1338
+ await server.connect(transport).catch(console.error);