mcp-probe-kit 3.0.24 → 3.2.0

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.
Files changed (142) hide show
  1. package/README.md +755 -779
  2. package/build/index.js +42 -41
  3. package/build/lib/__tests__/spec-validator.unit.test.js +115 -0
  4. package/build/lib/agents-md-template.js +32 -32
  5. package/build/lib/memory-orchestration.js +29 -8
  6. package/build/lib/skill-bridge.js +12 -12
  7. package/build/lib/spec-validator.d.ts +36 -0
  8. package/build/lib/spec-validator.js +103 -0
  9. package/build/lib/template-loader.js +149 -47
  10. package/build/lib/tool-annotations.d.ts +30 -0
  11. package/build/lib/tool-annotations.js +55 -0
  12. package/build/lib/toolset-manager.js +2 -0
  13. package/build/resources/index.d.ts +4 -0
  14. package/build/resources/index.js +4 -0
  15. package/build/resources/tool-params-guide.d.ts +571 -0
  16. package/build/resources/tool-params-guide.js +488 -0
  17. package/build/resources/ui-ux-data/guidelines/vercel-web-interface.json +1632 -1632
  18. package/build/resources/ui-ux-data/metadata.json +30 -30
  19. package/build/resources/ui-ux-data/shadcn/blocks.json +2541 -2541
  20. package/build/resources/ui-ux-data/shadcn/components.json +997 -997
  21. package/build/resources/ui-ux-data/themes/presets.json +483 -483
  22. package/build/schemas/index.d.ts +22 -22
  23. package/build/schemas/memory-tools.d.ts +0 -22
  24. package/build/schemas/memory-tools.js +0 -14
  25. package/build/schemas/project-tools.d.ts +22 -0
  26. package/build/schemas/project-tools.js +23 -0
  27. package/build/tools/analyze_project.d.ts +1 -0
  28. package/build/tools/analyze_project.js +527 -0
  29. package/build/tools/check_deps.d.ts +13 -0
  30. package/build/tools/check_deps.js +204 -0
  31. package/build/tools/check_spec.d.ts +7 -0
  32. package/build/tools/check_spec.js +81 -0
  33. package/build/tools/code_insight.js +41 -41
  34. package/build/tools/convert.d.ts +13 -0
  35. package/build/tools/convert.js +599 -0
  36. package/build/tools/css_order.d.ts +13 -0
  37. package/build/tools/css_order.js +81 -0
  38. package/build/tools/debug.d.ts +13 -0
  39. package/build/tools/debug.js +131 -0
  40. package/build/tools/design2code.d.ts +20 -0
  41. package/build/tools/design2code.js +426 -0
  42. package/build/tools/detect_shell.d.ts +6 -0
  43. package/build/tools/detect_shell.js +151 -0
  44. package/build/tools/explain.d.ts +13 -0
  45. package/build/tools/explain.js +390 -0
  46. package/build/tools/fix.d.ts +13 -0
  47. package/build/tools/fix.js +303 -0
  48. package/build/tools/fix_bug.js +161 -161
  49. package/build/tools/gen_mock.d.ts +22 -0
  50. package/build/tools/gen_mock.js +269 -0
  51. package/build/tools/gen_skill.d.ts +13 -0
  52. package/build/tools/gen_skill.js +560 -0
  53. package/build/tools/genapi.d.ts +13 -0
  54. package/build/tools/genapi.js +174 -0
  55. package/build/tools/genchangelog.d.ts +13 -0
  56. package/build/tools/genchangelog.js +250 -0
  57. package/build/tools/gencommit.js +60 -60
  58. package/build/tools/gendoc.d.ts +13 -0
  59. package/build/tools/gendoc.js +232 -0
  60. package/build/tools/genpr.d.ts +13 -0
  61. package/build/tools/genpr.js +194 -0
  62. package/build/tools/genreadme.d.ts +13 -0
  63. package/build/tools/genreadme.js +626 -0
  64. package/build/tools/gensql.d.ts +13 -0
  65. package/build/tools/gensql.js +320 -0
  66. package/build/tools/genui.d.ts +13 -0
  67. package/build/tools/genui.js +803 -0
  68. package/build/tools/index.d.ts +1 -1
  69. package/build/tools/index.js +1 -1
  70. package/build/tools/init_component_catalog.d.ts +22 -0
  71. package/build/tools/init_component_catalog.js +809 -0
  72. package/build/tools/init_project_context.js +432 -432
  73. package/build/tools/init_setting.d.ts +13 -0
  74. package/build/tools/init_setting.js +47 -0
  75. package/build/tools/perf.d.ts +13 -0
  76. package/build/tools/perf.js +409 -0
  77. package/build/tools/render_ui.d.ts +22 -0
  78. package/build/tools/render_ui.js +384 -0
  79. package/build/tools/resolve_conflict.d.ts +13 -0
  80. package/build/tools/resolve_conflict.js +349 -0
  81. package/build/tools/security_scan.d.ts +22 -0
  82. package/build/tools/security_scan.js +323 -0
  83. package/build/tools/split.d.ts +13 -0
  84. package/build/tools/split.js +599 -0
  85. package/build/tools/start_api.d.ts +13 -0
  86. package/build/tools/start_api.js +193 -0
  87. package/build/tools/start_bugfix.js +254 -243
  88. package/build/tools/start_doc.d.ts +13 -0
  89. package/build/tools/start_doc.js +207 -0
  90. package/build/tools/start_feature.js +162 -127
  91. package/build/tools/start_product.js +1 -1
  92. package/build/tools/start_refactor.d.ts +13 -0
  93. package/build/tools/start_refactor.js +188 -0
  94. package/build/tools/start_release.d.ts +13 -0
  95. package/build/tools/start_release.js +167 -0
  96. package/build/tools/start_review.d.ts +13 -0
  97. package/build/tools/start_review.js +175 -0
  98. package/build/tools/start_ui.js +426 -412
  99. package/build/tools/ui-ux-tools.js +290 -290
  100. package/build/utils/__tests__/vercel-guidelines-sync.unit.test.js +12 -12
  101. package/build/utils/themes-sync.js +8 -8
  102. package/package.json +81 -83
  103. package/build/lib/__tests__/memory-orchestration.unit.test.js +0 -88
  104. package/build/lib/__tests__/memory-payload.unit.test.js +0 -35
  105. package/build/lib/cursor-history-client.d.ts +0 -54
  106. package/build/lib/cursor-history-client.js +0 -240
  107. package/build/tools/__tests__/cursor-history.unit.test.js +0 -38
  108. package/build/tools/cursor_read_conversation.d.ts +0 -7
  109. package/build/tools/cursor_read_conversation.js +0 -36
  110. package/docs/.mcp-probe/layout.json +0 -11
  111. package/docs/CNAME +0 -1
  112. package/docs/assets/font/MaterialSymbolsOutlined.codepoints +0 -4102
  113. package/docs/assets/font/MaterialSymbolsOutlined.ttf +0 -0
  114. package/docs/assets/font/noto-sans-sc-400.ttf +0 -0
  115. package/docs/assets/font/noto-sans-sc-700.ttf +0 -0
  116. package/docs/assets/font/noto-sans-sc-900.ttf +0 -0
  117. package/docs/assets/js/i18n.js +0 -375
  118. package/docs/assets/js/tailwind.js +0 -83
  119. package/docs/assets/logo-zh.png +0 -0
  120. package/docs/assets/logo.png +0 -0
  121. package/docs/data/tools.js +0 -523
  122. package/docs/i18n/all-tools/en.json +0 -190
  123. package/docs/i18n/all-tools/ja.json +0 -171
  124. package/docs/i18n/all-tools/ko.json +0 -171
  125. package/docs/i18n/all-tools/zh-CN.json +0 -190
  126. package/docs/i18n/en.json +0 -626
  127. package/docs/i18n/ja.json +0 -602
  128. package/docs/i18n/ko.json +0 -602
  129. package/docs/i18n/zh-CN.json +0 -626
  130. package/docs/index.html +0 -327
  131. package/docs/memory-local-setup.md +0 -315
  132. package/docs/memory-local-setup.zh-CN.md +0 -283
  133. package/docs/pages/all-tools.html +0 -515
  134. package/docs/pages/examples.html +0 -717
  135. package/docs/pages/getting-started.html +0 -964
  136. package/docs/pages/migration.html +0 -308
  137. package/docs/specs/user-auth/design.md +0 -82
  138. package/docs/specs/user-auth/requirements.md +0 -52
  139. package/docs/specs/user-auth/tasks.md +0 -55
  140. /package/build/lib/__tests__/{memory-orchestration.unit.test.d.ts → spec-validator.unit.test.d.ts} +0 -0
  141. /package/build/{lib/__tests__/memory-payload.unit.test.d.ts → utils/design-docs-generator.d.ts} +0 -0
  142. /package/build/{tools/__tests__/cursor-history.unit.test.d.ts → utils/design-docs-generator.js} +0 -0
package/build/index.js CHANGED
@@ -5,10 +5,11 @@ import { InMemoryTaskMessageQueue, InMemoryTaskStore, } from "@modelcontextproto
5
5
  import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ProgressNotificationSchema, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
6
6
  import * as fs from "node:fs";
7
7
  import * as path from "node:path";
8
- import { initProject, gencommit, codeReview, codeInsight, gentest, refactor, initProjectContext, addFeature, fixBug, estimate, startFeature, startBugfix, startOnboard, startRalph, interview, askUser, uiDesignSystem, uiSearch, syncUiData, startUi, startProduct, gitWorkReport, searchMemory, readMemoryAsset, memorizeAsset, scanAndExtractPatterns, cursorReadConversation } from "./tools/index.js";
8
+ import { initProject, gencommit, codeReview, codeInsight, gentest, refactor, initProjectContext, addFeature, fixBug, estimate, checkSpec, startFeature, startBugfix, startOnboard, startRalph, interview, askUser, uiDesignSystem, uiSearch, syncUiData, startUi, startProduct, gitWorkReport, searchMemory, readMemoryAsset, memorizeAsset, scanAndExtractPatterns } from "./tools/index.js";
9
9
  import { VERSION, NAME } from "./version.js";
10
10
  import { allToolSchemas } from "./schemas/index.js";
11
11
  import { filterTools, getToolsetFromEnv } from "./lib/toolset-manager.js";
12
+ import { withToolAnnotations } from "./lib/tool-annotations.js";
12
13
  import { isAbortError, } from "./lib/tool-execution-context.js";
13
14
  const EXTENSIONS_CAPABILITY_KEY = "io.github.mybolide/extensions";
14
15
  const MAX_UI_APP_RESOURCES = 30;
@@ -116,43 +117,43 @@ function buildUiResourceHtml(name, args, result) {
116
117
  .join("\n\n")
117
118
  : "";
118
119
  const now = new Date().toISOString();
119
- return `<!doctype html>
120
- <html lang="zh-CN">
121
- <head>
122
- <meta charset="utf-8">
123
- <meta name="viewport" content="width=device-width, initial-scale=1">
124
- <title>${escapeHtml(name)} · MCP Apps</title>
125
- <style>
126
- :root { color-scheme: light; }
127
- body { font-family: "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif; margin: 0; background: #f4f7fb; color: #1e2a35; }
128
- .wrap { max-width: 960px; margin: 0 auto; padding: 24px; }
129
- .card { background: #fff; border-radius: 14px; padding: 18px; box-shadow: 0 4px 18px rgba(30,42,53,.08); margin-bottom: 16px; }
130
- h1 { margin: 0 0 8px; font-size: 24px; }
131
- h2 { margin: 0 0 10px; font-size: 16px; color: #2f4a65; }
132
- pre { white-space: pre-wrap; word-break: break-word; background: #0f1720; color: #d9e7f7; border-radius: 10px; padding: 12px; font-size: 12px; line-height: 1.45; }
133
- .meta { color: #4f6880; font-size: 12px; }
134
- </style>
135
- </head>
136
- <body>
137
- <div class="wrap">
138
- <div class="card">
139
- <h1>${escapeHtml(name)}</h1>
140
- <div class="meta">Generated at ${escapeHtml(now)} · MCP Apps preview</div>
141
- </div>
142
- <div class="card">
143
- <h2>Text Output</h2>
144
- <pre>${escapeHtml(textBlocks || "(no text output)")}</pre>
145
- </div>
146
- <div class="card">
147
- <h2>Structured Content</h2>
148
- <pre>${escapeHtml(structured)}</pre>
149
- </div>
150
- <div class="card">
151
- <h2>Arguments</h2>
152
- <pre>${escapeHtml(argJson)}</pre>
153
- </div>
154
- </div>
155
- </body>
120
+ return `<!doctype html>
121
+ <html lang="zh-CN">
122
+ <head>
123
+ <meta charset="utf-8">
124
+ <meta name="viewport" content="width=device-width, initial-scale=1">
125
+ <title>${escapeHtml(name)} · MCP Apps</title>
126
+ <style>
127
+ :root { color-scheme: light; }
128
+ body { font-family: "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif; margin: 0; background: #f4f7fb; color: #1e2a35; }
129
+ .wrap { max-width: 960px; margin: 0 auto; padding: 24px; }
130
+ .card { background: #fff; border-radius: 14px; padding: 18px; box-shadow: 0 4px 18px rgba(30,42,53,.08); margin-bottom: 16px; }
131
+ h1 { margin: 0 0 8px; font-size: 24px; }
132
+ h2 { margin: 0 0 10px; font-size: 16px; color: #2f4a65; }
133
+ pre { white-space: pre-wrap; word-break: break-word; background: #0f1720; color: #d9e7f7; border-radius: 10px; padding: 12px; font-size: 12px; line-height: 1.45; }
134
+ .meta { color: #4f6880; font-size: 12px; }
135
+ </style>
136
+ </head>
137
+ <body>
138
+ <div class="wrap">
139
+ <div class="card">
140
+ <h1>${escapeHtml(name)}</h1>
141
+ <div class="meta">Generated at ${escapeHtml(now)} · MCP Apps preview</div>
142
+ </div>
143
+ <div class="card">
144
+ <h2>Text Output</h2>
145
+ <pre>${escapeHtml(textBlocks || "(no text output)")}</pre>
146
+ </div>
147
+ <div class="card">
148
+ <h2>Structured Content</h2>
149
+ <pre>${escapeHtml(structured)}</pre>
150
+ </div>
151
+ <div class="card">
152
+ <h2>Arguments</h2>
153
+ <pre>${escapeHtml(argJson)}</pre>
154
+ </div>
155
+ </div>
156
+ </body>
156
157
  </html>`;
157
158
  }
158
159
  function putUiAppResource(toolName, args, result) {
@@ -410,7 +411,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
410
411
  const filteredTools = filterTools(allToolSchemas, toolset);
411
412
  console.error(`[MCP Probe Kit] 当前工具集: ${toolset} (${filteredTools.length}/${allToolSchemas.length} 个工具)`);
412
413
  return {
413
- tools: filteredTools,
414
+ tools: filteredTools.map(withToolAnnotations),
414
415
  };
415
416
  });
416
417
  async function executeTool(name, args, context) {
@@ -431,6 +432,8 @@ async function executeTool(name, args, context) {
431
432
  return await initProjectContext(args);
432
433
  case "add_feature":
433
434
  return await addFeature(args);
435
+ case "check_spec":
436
+ return await checkSpec(args);
434
437
  case "fix_bug":
435
438
  return await fixBug(args);
436
439
  case "estimate":
@@ -467,8 +470,6 @@ async function executeTool(name, args, context) {
467
470
  return await memorizeAsset(args);
468
471
  case "scan_and_extract_patterns":
469
472
  return await scanAndExtractPatterns(args);
470
- case "cursor_read_conversation":
471
- return await cursorReadConversation(args);
472
473
  default:
473
474
  throw new Error(`未知工具: ${name}`);
474
475
  }
@@ -0,0 +1,115 @@
1
+ import { describe, test, expect } from 'vitest';
2
+ import { validateSpecDocuments, extractFrIds } from '../spec-validator.js';
3
+ const goodReq = `# 需求文档:demo
4
+
5
+ ## 功能概述
6
+ 做一个登录功能。
7
+
8
+ ## 历史经验与坑(来自记忆库)
9
+ - 可复用经验: 无
10
+ - 必须规避的坑: 无
11
+
12
+ ## 范围边界
13
+ In Scope: 登录
14
+ Out of Scope: 注册
15
+
16
+ ## 需求列表
17
+ ### FR-1: 登录
18
+ **优先级:** Must
19
+ **用户故事:** 作为用户,我想登录,以便访问系统。
20
+ #### 验收标准(EARS)
21
+ 1. WHEN 提交正确凭证 THEN 系统 SHALL 登录成功
22
+
23
+ ## 非功能需求
24
+ - NFR-1: 登录响应 < 1s
25
+
26
+ ## 依赖关系
27
+ - 无
28
+ `;
29
+ const goodDesign = `# 设计文档:demo
30
+
31
+ ## 概述
32
+ 登录设计。
33
+ **对应需求:** FR-1
34
+
35
+ ## 技术方案
36
+ ### 技术选型
37
+ 使用 JWT。
38
+
39
+ ## 数据模型
40
+ 不涉及。
41
+
42
+ ## API 设计
43
+ POST /api/login
44
+
45
+ ## 文件结构
46
+ src/login.ts
47
+
48
+ ## 设计决策
49
+ 决策 1:用 JWT。
50
+
51
+ ## 风险评估
52
+ 无。
53
+ `;
54
+ const goodTasks = `# 任务清单:demo
55
+
56
+ ## 概述
57
+ 任务分解。
58
+
59
+ ## 任务列表
60
+ ### 阶段 2: 核心实现
61
+ - [ ] 2.1 实现登录接口 — _需求: FR-1_ | _设计: API 设计_
62
+
63
+ ## 检查点
64
+ - [ ] 阶段 2 完成后:登录可用
65
+
66
+ ## 需求覆盖矩阵
67
+ | 需求 ID | 设计章节 | 任务编号 | 状态 |
68
+ | FR-1 | API 设计 | 2.1 | 未开始 |
69
+
70
+ ## 文件变更清单
71
+ | 文件 | 操作 | 说明 |
72
+ | src/login.ts | 新建 | 登录接口 |
73
+ `;
74
+ describe('spec-validator(P1 填写后校验)', () => {
75
+ test('完整规格通过校验', () => {
76
+ const r = validateSpecDocuments({ requirements: goodReq, design: goodDesign, tasks: goodTasks });
77
+ expect(r.passed).toBe(true);
78
+ expect(r.errorCount).toBe(0);
79
+ expect(r.frIds).toContain('FR-1');
80
+ });
81
+ test('残留 [填写:] 占位 → 未通过', () => {
82
+ const r = validateSpecDocuments({ requirements: goodReq + '\n- [填写:补充]', design: goodDesign, tasks: goodTasks });
83
+ expect(r.passed).toBe(false);
84
+ expect(r.issues.some((i) => i.file === 'requirements' && i.code === 'placeholder')).toBe(true);
85
+ });
86
+ test('缺少 design 文件 → 未通过', () => {
87
+ const r = validateSpecDocuments({ requirements: goodReq, design: null, tasks: goodTasks });
88
+ expect(r.passed).toBe(false);
89
+ expect(r.issues.some((i) => i.file === 'design' && i.code === 'missing_file')).toBe(true);
90
+ });
91
+ test('无 EARS 验收标准 → 未通过', () => {
92
+ const r = validateSpecDocuments({
93
+ requirements: goodReq.replace('1. WHEN 提交正确凭证 THEN 系统 SHALL 登录成功', '1. 能登录'),
94
+ design: goodDesign,
95
+ tasks: goodTasks,
96
+ });
97
+ expect(r.passed).toBe(false);
98
+ expect(r.issues.some((i) => i.code === 'no_acceptance')).toBe(true);
99
+ });
100
+ test('FR 未进 tasks 覆盖矩阵 → 跨文档未覆盖', () => {
101
+ const reqTwoFr = goodReq.replace('## 非功能需求', `### FR-2: 登出
102
+ **优先级:** Should
103
+ **用户故事:** 作为用户,我想登出。
104
+ #### 验收标准(EARS)
105
+ 1. WHEN 点击登出 THEN 系统 SHALL 退出登录
106
+
107
+ ## 非功能需求`);
108
+ const r = validateSpecDocuments({ requirements: reqTwoFr, design: goodDesign, tasks: goodTasks });
109
+ expect(r.passed).toBe(false);
110
+ expect(r.issues.some((i) => i.code === 'uncovered_fr')).toBe(true);
111
+ });
112
+ test('extractFrIds 去重并保序', () => {
113
+ expect(extractFrIds('FR-1 写了 FR-1 又写 FR-2')).toEqual(['FR-1', 'FR-2']);
114
+ });
115
+ });
@@ -4,18 +4,18 @@ function link(layout, targetRel) {
4
4
  }
5
5
  function memorySection(locale) {
6
6
  if (locale === "zh-CN") {
7
- return `
8
- 记忆(需 MEMORY_QDRANT_URL 等已配置):
9
- - 检索:\`start_*\` 命中后**自动注入**历史经验全文;中途补查可用 \`search_memory\`;单条精读仍可用 \`read_memory_asset\`
10
- - 沉淀:跨仓库共享**勿填** source_project/source_path;路径写进 content;summary 写检索关键词
11
- - Bug 修完验证通过 → **必须** \`memorize_asset\` type=\`bugfix\` tags=\`bugfix,root-cause\`(content 含【现象】【根因】【修复】【验证】)
7
+ return `
8
+ 记忆(需 MEMORY_QDRANT_URL 等已配置):
9
+ - 检索:\`start_*\` 命中后**自动注入**历史经验全文;中途补查可用 \`search_memory\`;单条精读仍可用 \`read_memory_asset\`
10
+ - 沉淀:跨仓库共享**勿填** source_project/source_path;路径写进 content;summary 写检索关键词
11
+ - Bug 修完验证通过 → **必须** \`memorize_asset\` type=\`bugfix\` tags=\`bugfix,root-cause\`(content 含【现象】【根因】【修复】【验证】)
12
12
  - 功能/UI 可复用产出 → \`memorize_asset\` type=\`pattern\`/\`component\``;
13
13
  }
14
- return `
15
- Memory (requires MEMORY_* env):
16
- - Search: \`start_*\` auto-injects full memory hits; use \`search_memory\` mid-task; \`read_memory_asset\` for a specific id
17
- - Store: do NOT use source_project/source_path for cross-repo pools; put paths in content; write keyword-rich summary
18
- - After verified bugfix → MUST \`memorize_asset\` type=\`bugfix\` (sections: symptom, root cause, fix, verification)
14
+ return `
15
+ Memory (requires MEMORY_* env):
16
+ - Search: \`start_*\` auto-injects full memory hits; use \`search_memory\` mid-task; \`read_memory_asset\` for a specific id
17
+ - Store: do NOT use source_project/source_path for cross-repo pools; put paths in content; write keyword-rich summary
18
+ - After verified bugfix → MUST \`memorize_asset\` type=\`bugfix\` (sections: symptom, root cause, fix, verification)
19
19
  - Reusable feature/UI → \`memorize_asset\` type=\`pattern\`/\`component\``;
20
20
  }
21
21
  /**
@@ -26,30 +26,30 @@ export function generateAgentsMdInner(input) {
26
26
  const graph = link(layout, layout.latestMarkdownPath);
27
27
  const ctxIndex = link(layout, layout.legacyIndexPath);
28
28
  if (locale === "zh-CN") {
29
- return `## MCP(必须先调)
30
- 需已配置 mcp-probe-kit。\`start_*\` 若返回 delegated plan,逐步执行完再结束。
31
-
32
- - 新功能 → \`start_feature\`(会先搜记忆)
33
- - Bug → \`start_bugfix\`(会先搜记忆)
34
- - UI → \`start_ui\`(会先搜记忆)
35
- - 不熟代码 / 影响面 → \`code_insight\`(context / impact / auto)
36
- - 缺上下文 → \`init_project_context\`
37
- - 提交 → \`gencommit\`
38
-
39
- 上下文:写代码前先读 [project-context](${ctxIndex})(链到 \`${layout.modularDir}/\` 各文档)
29
+ return `## MCP(必须先调)
30
+ 需已配置 mcp-probe-kit。\`start_*\` 若返回 delegated plan,逐步执行完再结束。
31
+
32
+ - 新功能 → \`start_feature\`(会先搜记忆)
33
+ - Bug → \`start_bugfix\`(会先搜记忆)
34
+ - UI → \`start_ui\`(会先搜记忆)
35
+ - 不熟代码 / 影响面 → \`code_insight\`(context / impact / auto)
36
+ - 缺上下文 → \`init_project_context\`
37
+ - 提交 → \`gencommit\`
38
+
39
+ 上下文:写代码前先读 [project-context](${ctxIndex})(链到 \`${layout.modularDir}/\` 各文档)
40
40
  图谱:大改前读 [latest](${graph});过期 \`code_insight\` mode=auto save_to_docs=true${memorySection(locale)}`;
41
41
  }
42
- return `## MCP (call first)
43
- Requires mcp-probe-kit. Complete every \`start_*\` delegated plan step before done.
44
-
45
- - Feature → \`start_feature\` (searches memory first)
46
- - Bug → \`start_bugfix\` (searches memory first)
47
- - UI → \`start_ui\` (searches memory first)
48
- - Unfamiliar code / impact → \`code_insight\` (context / impact / auto)
49
- - Missing context → \`init_project_context\`
50
- - Commit → \`gencommit\`
51
-
52
- Context: before coding read [project-context](${ctxIndex}) (links to \`${layout.modularDir}/\`)
42
+ return `## MCP (call first)
43
+ Requires mcp-probe-kit. Complete every \`start_*\` delegated plan step before done.
44
+
45
+ - Feature → \`start_feature\` (searches memory first)
46
+ - Bug → \`start_bugfix\` (searches memory first)
47
+ - UI → \`start_ui\` (searches memory first)
48
+ - Unfamiliar code / impact → \`code_insight\` (context / impact / auto)
49
+ - Missing context → \`init_project_context\`
50
+ - Commit → \`gencommit\`
51
+
52
+ Context: before coding read [project-context](${ctxIndex}) (links to \`${layout.modularDir}/\`)
53
53
  Graph: read [latest](${graph}) before large changes; refresh \`code_insight\` mode=auto save_to_docs=true${memorySection(locale)}`;
54
54
  }
55
55
  export function generateAgentsMdTemplate(input) {
@@ -5,9 +5,11 @@ function kindSearchPreferences(kind) {
5
5
  case 'bugfix':
6
6
  return { preferTypes: ['bugfix'], preferTags: ['bugfix', 'root-cause'] };
7
7
  case 'ui':
8
- return { preferTypes: ['component', 'pattern'], preferTags: ['ui', 'pattern'] };
8
+ // UI 时也优先捞历史坑(bugfix),避免重复踩同类交互/兼容性坑
9
+ return { preferTypes: ['component', 'pattern', 'bugfix'], preferTags: ['ui', 'pattern', 'root-cause'] };
9
10
  case 'feature':
10
- return { preferTypes: ['pattern', 'code'], preferTags: ['feature', 'pattern'] };
11
+ // 做新功能时同时捞「可复用模式」与「历史坑」,让规划前就看到经验与雷区
12
+ return { preferTypes: ['pattern', 'code', 'bugfix'], preferTags: ['feature', 'pattern', 'root-cause'] };
11
13
  default:
12
14
  return { preferTypes: [], preferTags: [] };
13
15
  }
@@ -43,7 +45,11 @@ export async function loadMemoryInjectionContext(query, kind = 'default') {
43
45
  }
44
46
  try {
45
47
  const prefs = kindSearchPreferences(kind);
48
+ // feature/ui 同时要「经验」与「坑」两类,默认条数偏少容易只剩一类;放宽下限让两组都有空间
49
+ const baseLimit = getMemoryConfig().searchLimit;
50
+ const injectionLimit = (kind === 'feature' || kind === 'ui') ? Math.max(baseLimit, 5) : baseLimit;
46
51
  const results = await client.search(query, {
52
+ limit: injectionLimit,
47
53
  preferTypes: prefs.preferTypes,
48
54
  preferTags: prefs.preferTags,
49
55
  });
@@ -157,22 +163,37 @@ function formatResultBlock(item, index, context, config) {
157
163
  }
158
164
  return `${header}\n - 全文加载失败,可手动: read_memory_asset {"asset_id": "${item.id}"}\n`;
159
165
  }
166
+ function isPitfallResult(item) {
167
+ return item.type === 'bugfix' || item.tags.includes('bugfix') || item.tags.includes('root-cause');
168
+ }
160
169
  export function renderMemoryGuideSection(context) {
161
170
  const config = getMemoryConfig();
162
171
  if (!context.enabled) {
163
172
  return '';
164
173
  }
165
174
  if (!context.available) {
166
- return `\n\n## 🧠 记忆系统\n- 状态: 已配置但本次检索降级\n- 原因: ${context.error || '未知错误'}\n- 处理: 忽略记忆注入,继续主流程\n`;
175
+ return `\n\n## 🧠 历史经验与坑(记忆库)\n- 状态: 已配置但本次检索降级\n- 原因: ${context.error || '未知错误'}\n- 处理: 忽略记忆注入,继续主流程\n`;
167
176
  }
168
177
  if (context.results.length === 0) {
169
- return `\n\n## 🧠 记忆系统\n- 状态: 已启用\n- 检索结果: 未找到高相关记录(含历史 Bug 修复与可复用模式)\n- 处理: 继续主流程;Bug 修复验证通过后必须 \`memorize_asset\` 沉淀;功能/UI 有可复用产出再沉淀\n`;
178
+ return `\n\n## 🧠 历史经验与坑(记忆库)\n- 状态: 已启用\n- 检索结果: 未找到高相关记录(含历史 Bug 修复与可复用模式)\n- 处理: 继续主流程;Bug 修复验证通过后必须 \`memorize_asset\` 沉淀;功能/UI 有可复用产出再沉淀\n`;
170
179
  }
171
180
  const loadedCount = context.results.filter((item) => Boolean(context.assetsById[item.id])).length;
172
- const items = context.results
173
- .map((item, index) => formatResultBlock(item, index, context, config))
174
- .join('\n');
175
- return `\n\n## 🧠 记忆系统\n- 状态: 已启用\n- 指令: 下列为已自动加载的历史经验全文(${loadedCount}/${context.results.length} 条);开干前直接复用,无需再调 \`read_memory_asset\`\n- 检索结果:\n${items}`;
181
+ const pitfalls = context.results.filter(isPitfallResult);
182
+ const experiences = context.results.filter((item) => !isPitfallResult(item));
183
+ const renderGroup = (items) => items.map((item, index) => formatResultBlock(item, index, context, config)).join('\n');
184
+ const blocks = [
185
+ `\n\n## 🧠 历史经验与坑(记忆库)`,
186
+ `- 状态: 已启用`,
187
+ `- 指令: 下列为已自动加载的历史经验全文(${loadedCount}/${context.results.length} 条);开干前直接复用,无需再调 \`read_memory_asset\``,
188
+ `- 用法: 先逐条核对「历史坑」是否已在本次设计中规避,再复用「可复用经验」,据此收敛需求范围`,
189
+ ];
190
+ if (pitfalls.length > 0) {
191
+ blocks.push(`\n### ⚠️ 历史坑(务必规避,共 ${pitfalls.length} 条)\n${renderGroup(pitfalls)}`);
192
+ }
193
+ if (experiences.length > 0) {
194
+ blocks.push(`\n### ♻️ 可复用经验 / 相关历史(共 ${experiences.length} 条)\n${renderGroup(experiences)}`);
195
+ }
196
+ return blocks.join('\n');
176
197
  }
177
198
  export function buildMemoryPlanStep(kind = 'default') {
178
199
  if (kind === 'bugfix') {
@@ -84,17 +84,17 @@ export function renderSkillBridgeSection(status) {
84
84
  const conclusion = status.ready
85
85
  ? "全部 skill 可用,建议按顺序调用后再执行 MCP 工具步骤。"
86
86
  : "部分 skill 缺失:继续执行 MCP 主流程,不阻塞;安装缺失 skill 后下次运行可获得更高质量输出。";
87
- return `## 🧩 Skill Bridge(UI/PRD 增强)
88
-
89
- 按顺序调用(宿主支持时):
90
- ${orderLines}
91
-
92
- 当前状态:
93
- ${stateLines}
94
-
95
- ${conclusion}
96
-
97
- ---
98
-
87
+ return `## 🧩 Skill Bridge(UI/PRD 增强)
88
+
89
+ 按顺序调用(宿主支持时):
90
+ ${orderLines}
91
+
92
+ 当前状态:
93
+ ${stateLines}
94
+
95
+ ${conclusion}
96
+
97
+ ---
98
+
99
99
  `;
100
100
  }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * 规格文档「填写后校验」闸门(P1)
3
+ *
4
+ * 目标:不依赖模型自觉。落盘后机械校验 requirements/design/tasks 的完整性,
5
+ * 打回残留占位符、缺章节、无 FR、无验收标准、FR 未进覆盖矩阵等问题,
6
+ * 让中等/偏弱模型也能被强制补全到可实现的程度。
7
+ *
8
+ * 本模块为纯逻辑、无 I/O,便于单测。
9
+ */
10
+ export type SpecFileKey = 'requirements' | 'design' | 'tasks';
11
+ export interface SpecFileInput {
12
+ requirements?: string | null;
13
+ design?: string | null;
14
+ tasks?: string | null;
15
+ }
16
+ export interface SpecIssue {
17
+ file: SpecFileKey | 'cross';
18
+ severity: 'error' | 'warning';
19
+ code: string;
20
+ message: string;
21
+ }
22
+ export interface SpecValidationReport {
23
+ passed: boolean;
24
+ errorCount: number;
25
+ warningCount: number;
26
+ issues: SpecIssue[];
27
+ /** 从 requirements 提取的需求 ID(FR-1、FR-2…) */
28
+ frIds: string[];
29
+ summary: string;
30
+ }
31
+ /** 提取去重后的 FR-n 需求 ID */
32
+ export declare function extractFrIds(content: string): string[];
33
+ /**
34
+ * 校验三份规格文档。传入各文件的全文(不存在传 null)。
35
+ */
36
+ export declare function validateSpecDocuments(input: SpecFileInput): SpecValidationReport;
@@ -0,0 +1,103 @@
1
+ /**
2
+ * 规格文档「填写后校验」闸门(P1)
3
+ *
4
+ * 目标:不依赖模型自觉。落盘后机械校验 requirements/design/tasks 的完整性,
5
+ * 打回残留占位符、缺章节、无 FR、无验收标准、FR 未进覆盖矩阵等问题,
6
+ * 让中等/偏弱模型也能被强制补全到可实现的程度。
7
+ *
8
+ * 本模块为纯逻辑、无 I/O,便于单测。
9
+ */
10
+ /** 匹配未填写的占位符,如 `[填写:xxx]` / `[填写:xxx]` */
11
+ const PLACEHOLDER_RE = /\[填写[::][^\]]*\]/g;
12
+ function countPlaceholders(content) {
13
+ const matches = content.match(PLACEHOLDER_RE);
14
+ return matches ? matches.length : 0;
15
+ }
16
+ function escapeRegExp(value) {
17
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
18
+ }
19
+ /** 标题行存在性(## 概述 等) */
20
+ function hasSection(content, name) {
21
+ const re = new RegExp(`^#{1,6}\\s+${escapeRegExp(name)}`, 'm');
22
+ return re.test(content);
23
+ }
24
+ /** 提取去重后的 FR-n 需求 ID */
25
+ export function extractFrIds(content) {
26
+ const matches = content.match(/\bFR-\d+\b/g) || [];
27
+ return [...new Set(matches)];
28
+ }
29
+ const REQUIRED_SECTIONS = {
30
+ requirements: ['功能概述', '需求列表', '非功能需求', '依赖关系'],
31
+ design: ['概述', '技术方案', '文件结构'],
32
+ tasks: ['任务列表', '需求覆盖矩阵'],
33
+ };
34
+ /**
35
+ * 校验三份规格文档。传入各文件的全文(不存在传 null)。
36
+ */
37
+ export function validateSpecDocuments(input) {
38
+ const issues = [];
39
+ const requirements = input.requirements ?? null;
40
+ const design = input.design ?? null;
41
+ const tasks = input.tasks ?? null;
42
+ const checkFile = (key, content) => {
43
+ if (!content || !content.trim()) {
44
+ issues.push({ file: key, severity: 'error', code: 'missing_file', message: `${key}.md 不存在或为空` });
45
+ return;
46
+ }
47
+ const placeholders = countPlaceholders(content);
48
+ if (placeholders > 0) {
49
+ issues.push({
50
+ file: key,
51
+ severity: 'error',
52
+ code: 'placeholder',
53
+ message: `${key}.md 仍有 ${placeholders} 处未填写的「[填写:…]」占位`,
54
+ });
55
+ }
56
+ for (const section of REQUIRED_SECTIONS[key]) {
57
+ if (!hasSection(content, section)) {
58
+ issues.push({ file: key, severity: 'error', code: 'missing_section', message: `${key}.md 缺少章节「${section}」` });
59
+ }
60
+ }
61
+ };
62
+ checkFile('requirements', requirements);
63
+ checkFile('design', design);
64
+ checkFile('tasks', tasks);
65
+ // requirements:需有带 ID 的需求与 EARS 验收标准
66
+ let frIds = [];
67
+ if (requirements && requirements.trim()) {
68
+ frIds = extractFrIds(requirements);
69
+ if (frIds.length === 0) {
70
+ issues.push({ file: 'requirements', severity: 'error', code: 'no_fr', message: 'requirements.md 未定义任何带稳定 ID 的需求(FR-1、FR-2…)' });
71
+ }
72
+ if (!/SHALL/i.test(requirements)) {
73
+ issues.push({ file: 'requirements', severity: 'error', code: 'no_acceptance', message: 'requirements.md 未发现 EARS 验收标准(应包含「SHALL」)' });
74
+ }
75
+ }
76
+ // design:应引用 requirements 的 FR
77
+ if (design && design.trim() && frIds.length > 0) {
78
+ const designFr = extractFrIds(design);
79
+ if (designFr.length === 0) {
80
+ issues.push({ file: 'design', severity: 'warning', code: 'no_fr_ref', message: 'design.md 未引用任何 FR-id(应在「对应需求」标注本设计覆盖的需求)' });
81
+ }
82
+ }
83
+ // 跨文档:每条 FR 都应在 tasks(含覆盖矩阵)出现,否则可能漏实现
84
+ if (requirements && tasks && tasks.trim() && frIds.length > 0) {
85
+ const taskFr = new Set(extractFrIds(tasks));
86
+ const uncovered = frIds.filter((id) => !taskFr.has(id));
87
+ if (uncovered.length > 0) {
88
+ issues.push({
89
+ file: 'cross',
90
+ severity: 'error',
91
+ code: 'uncovered_fr',
92
+ message: `以下需求未在 tasks.md(含需求覆盖矩阵)出现,可能漏实现:${uncovered.join(', ')}`,
93
+ });
94
+ }
95
+ }
96
+ const errorCount = issues.filter((item) => item.severity === 'error').length;
97
+ const warningCount = issues.filter((item) => item.severity === 'warning').length;
98
+ const passed = errorCount === 0;
99
+ const summary = passed
100
+ ? `规格校验通过(${frIds.length} 条需求${warningCount ? `,${warningCount} 个提醒` : ''})`
101
+ : `规格校验未通过:${errorCount} 个必须修复的问题${warningCount ? `、${warningCount} 个提醒` : ''}`;
102
+ return { passed, errorCount, warningCount, issues, frIds, summary };
103
+ }