mcp-probe-kit 3.1.0 → 3.3.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.
- package/README.md +15 -5
- package/build/index.js +5 -2
- package/build/lib/__tests__/memory-orchestration.unit.test.js +88 -0
- package/build/lib/__tests__/memory-payload.unit.test.js +35 -0
- package/build/lib/__tests__/quality-constraints.unit.test.d.ts +1 -0
- package/build/lib/__tests__/quality-constraints.unit.test.js +54 -0
- package/build/lib/__tests__/spec-validator.unit.test.d.ts +1 -0
- package/build/lib/__tests__/spec-validator.unit.test.js +147 -0
- package/build/lib/agents-md-template.js +32 -32
- package/build/lib/cursor-history-client.d.ts +54 -0
- package/build/lib/cursor-history-client.js +240 -0
- package/build/lib/memory-orchestration.js +29 -8
- package/build/lib/quality-constraints.d.ts +54 -0
- package/build/lib/quality-constraints.js +155 -0
- package/build/lib/skill-bridge.js +12 -12
- package/build/lib/spec-validator.d.ts +36 -0
- package/build/lib/spec-validator.js +116 -0
- package/build/lib/template-loader.js +223 -61
- package/build/lib/tool-annotations.d.ts +30 -0
- package/build/lib/tool-annotations.js +55 -0
- package/build/lib/toolset-manager.js +2 -0
- package/build/resources/ui-ux-data/guidelines/vercel-web-interface.json +1632 -1632
- package/build/resources/ui-ux-data/metadata.json +30 -30
- package/build/resources/ui-ux-data/shadcn/blocks.json +2541 -2541
- package/build/resources/ui-ux-data/shadcn/components.json +997 -997
- package/build/resources/ui-ux-data/themes/presets.json +483 -483
- package/build/schemas/index.d.ts +22 -0
- package/build/schemas/project-tools.d.ts +22 -0
- package/build/schemas/project-tools.js +23 -0
- package/build/tools/__tests__/cursor-history.unit.test.d.ts +1 -0
- package/build/tools/__tests__/cursor-history.unit.test.js +38 -0
- package/build/tools/check_spec.d.ts +7 -0
- package/build/tools/check_spec.js +81 -0
- package/build/tools/code_insight.js +41 -41
- package/build/tools/code_review.js +11 -4
- package/build/tools/cursor_read_conversation.d.ts +7 -0
- package/build/tools/cursor_read_conversation.js +36 -0
- package/build/tools/fix_bug.js +161 -161
- package/build/tools/gencommit.js +60 -60
- package/build/tools/index.d.ts +1 -0
- package/build/tools/index.js +1 -0
- package/build/tools/init_project_context.js +432 -432
- package/build/tools/start_bugfix.js +21 -10
- package/build/tools/start_feature.js +46 -11
- package/build/tools/start_product.js +1 -1
- package/build/tools/start_ui.js +44 -13
- package/build/tools/ui-ux-tools.d.ts +3 -0
- package/build/tools/ui-ux-tools.js +302 -290
- package/build/utils/__tests__/vercel-guidelines-sync.unit.test.js +12 -12
- package/build/utils/design-reasoning-engine.d.ts +2 -0
- package/build/utils/design-reasoning-engine.js +3 -0
- package/build/utils/themes-sync.js +8 -8
- package/package.json +4 -3
- package/build/resources/index.d.ts +0 -4
- package/build/resources/index.js +0 -4
- package/build/resources/tool-params-guide.d.ts +0 -571
- package/build/resources/tool-params-guide.js +0 -488
- package/build/tools/analyze_project.d.ts +0 -1
- package/build/tools/analyze_project.js +0 -527
- package/build/tools/check_deps.d.ts +0 -13
- package/build/tools/check_deps.js +0 -204
- package/build/tools/convert.d.ts +0 -13
- package/build/tools/convert.js +0 -599
- package/build/tools/css_order.d.ts +0 -13
- package/build/tools/css_order.js +0 -81
- package/build/tools/debug.d.ts +0 -13
- package/build/tools/debug.js +0 -131
- package/build/tools/design2code.d.ts +0 -20
- package/build/tools/design2code.js +0 -426
- package/build/tools/detect_shell.d.ts +0 -6
- package/build/tools/detect_shell.js +0 -151
- package/build/tools/explain.d.ts +0 -13
- package/build/tools/explain.js +0 -390
- package/build/tools/fix.d.ts +0 -13
- package/build/tools/fix.js +0 -303
- package/build/tools/gen_mock.d.ts +0 -22
- package/build/tools/gen_mock.js +0 -269
- package/build/tools/gen_skill.d.ts +0 -13
- package/build/tools/gen_skill.js +0 -560
- package/build/tools/genapi.d.ts +0 -13
- package/build/tools/genapi.js +0 -174
- package/build/tools/genchangelog.d.ts +0 -13
- package/build/tools/genchangelog.js +0 -250
- package/build/tools/gendoc.d.ts +0 -13
- package/build/tools/gendoc.js +0 -232
- package/build/tools/genpr.d.ts +0 -13
- package/build/tools/genpr.js +0 -194
- package/build/tools/genreadme.d.ts +0 -13
- package/build/tools/genreadme.js +0 -626
- package/build/tools/gensql.d.ts +0 -13
- package/build/tools/gensql.js +0 -320
- package/build/tools/genui.d.ts +0 -13
- package/build/tools/genui.js +0 -803
- package/build/tools/init_component_catalog.d.ts +0 -22
- package/build/tools/init_component_catalog.js +0 -809
- package/build/tools/init_setting.d.ts +0 -13
- package/build/tools/init_setting.js +0 -47
- package/build/tools/perf.d.ts +0 -13
- package/build/tools/perf.js +0 -409
- package/build/tools/render_ui.d.ts +0 -22
- package/build/tools/render_ui.js +0 -384
- package/build/tools/resolve_conflict.d.ts +0 -13
- package/build/tools/resolve_conflict.js +0 -349
- package/build/tools/security_scan.d.ts +0 -22
- package/build/tools/security_scan.js +0 -323
- package/build/tools/split.d.ts +0 -13
- package/build/tools/split.js +0 -599
- package/build/tools/start_api.d.ts +0 -13
- package/build/tools/start_api.js +0 -193
- package/build/tools/start_doc.d.ts +0 -13
- package/build/tools/start_doc.js +0 -207
- package/build/tools/start_refactor.d.ts +0 -13
- package/build/tools/start_refactor.js +0 -188
- package/build/tools/start_release.d.ts +0 -13
- package/build/tools/start_release.js +0 -167
- package/build/tools/start_review.d.ts +0 -13
- package/build/tools/start_review.js +0 -175
- /package/build/{utils/design-docs-generator.d.ts → lib/__tests__/memory-orchestration.unit.test.d.ts} +0 -0
- /package/build/{utils/design-docs-generator.js → lib/__tests__/memory-payload.unit.test.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
|
|
27
27
|
> 🚀 AI-Powered Complete Development Toolkit - Covering the Entire Development Lifecycle
|
|
28
28
|
|
|
29
|
-
A powerful MCP (Model Context Protocol) server providing **
|
|
29
|
+
A powerful MCP (Model Context Protocol) server providing **27 tools** covering the complete workflow from product analysis to final release (Requirements → Design → Development → Quality → Release), all tools support **structured output**.
|
|
30
30
|
|
|
31
31
|
**🎉 v3.0 Major Update**: Streamlined tool count, focus on core competencies, eliminate choice paralysis, let AI do more native work
|
|
32
32
|
|
|
@@ -42,7 +42,7 @@ A powerful MCP (Model Context Protocol) server providing **26 tools** covering t
|
|
|
42
42
|
|
|
43
43
|
- [Quick Start](https://mcp-probe-kit.bytezonex.com/pages/getting-started.html) - Setup in 5 minutes
|
|
44
44
|
- [Local Memory Stack (Qdrant + Nomic Embed)](docs/memory-local-setup.md) - Docker Compose, ports `50008` / `50012`, MCP env
|
|
45
|
-
- [All Tools](https://mcp-probe-kit.bytezonex.com/pages/all-tools.html) - Complete list of
|
|
45
|
+
- [All Tools](https://mcp-probe-kit.bytezonex.com/pages/all-tools.html) - Complete list of 27 tools
|
|
46
46
|
- [Best Practices](https://mcp-probe-kit.bytezonex.com/pages/examples.html) - Full development workflow guide
|
|
47
47
|
- [v3.0 Migration Guide](https://mcp-probe-kit.bytezonex.com/pages/migration.html) - Upgrade from v2.x to v3.0
|
|
48
48
|
|
|
@@ -50,7 +50,7 @@ A powerful MCP (Model Context Protocol) server providing **26 tools** covering t
|
|
|
50
50
|
|
|
51
51
|
## ✨ Core Features
|
|
52
52
|
|
|
53
|
-
### 📦
|
|
53
|
+
### 📦 27 Tools
|
|
54
54
|
|
|
55
55
|
- **🔄 Workflow Orchestration** (6 tools) - One-click complex development workflows
|
|
56
56
|
- `start_feature`, `start_bugfix`, `start_onboard`, `start_ui`, `start_product`, `start_ralph`
|
|
@@ -60,13 +60,23 @@ A powerful MCP (Model Context Protocol) server providing **26 tools** covering t
|
|
|
60
60
|
- `gencommit`, `git_work_report`
|
|
61
61
|
- **⚡ Code Generation** (1 tool) - Test generation
|
|
62
62
|
- `gentest`
|
|
63
|
-
- **📦 Project Management** (
|
|
64
|
-
- `init_project`, `init_project_context`, `add_feature`, `estimate`, `interview`, `ask_user`
|
|
63
|
+
- **📦 Project Management** (7 tools) - Project initialization, requirements, and spec validation
|
|
64
|
+
- `init_project`, `init_project_context`, `add_feature`, `check_spec`, `estimate`, `interview`, `ask_user`
|
|
65
65
|
- **🎨 UI/UX Utilities** (3 tools) - Design systems and UI data synchronization
|
|
66
66
|
- `ui_design_system`, `ui_search`, `sync_ui_data`
|
|
67
67
|
- **🧠 Memory** (4 tools) - Reusable asset memory
|
|
68
68
|
- `search_memory`, `read_memory_asset`, `memorize_asset`, `scan_and_extract_patterns`
|
|
69
69
|
|
|
70
|
+
### 🛡️ Quality Constraints (single source of truth)
|
|
71
|
+
|
|
72
|
+
All hard quality rules live in one module (`src/lib/quality-constraints.ts`) and are injected into `code_review`, the `add_feature` task templates, and the UI tools. Change once, apply everywhere — inspired by [taste-skill](https://github.com/Leonxlnx/taste-skill) and [impeccable](https://github.com/pbakaus/impeccable).
|
|
73
|
+
|
|
74
|
+
- **Code limits**: single file ≤ 500 lines (split into modules/components when exceeded), function ≤ 50 lines, nesting ≤ 4, parameters ≤ 3.
|
|
75
|
+
- **Completeness blacklist**: `code_review` flags placeholder/elision patterns (`// ...`, `// TODO`, `// rest of code`, bare `...`) as CRITICAL — "a partial output is a broken output".
|
|
76
|
+
- **Anti-laziness task templates**: `add_feature` tasks now carry a Scope-lock deliverable count, a mandatory evidence block (read code before writing), a per-file line budget, and a binary zero-tolerance rule for placeholders. `check_spec` validates these (missing Scope-lock = error, thin task without evidence = warning).
|
|
77
|
+
- **UI hard red lines**: numeric, machine-checkable rules — 4pt spacing scale, WCAG contrast (4.5/3/3), type scale ≥ 1.25, hero font ≤ 6rem, OKLCH, eight interaction states, cognitive load ≤ 4, motion 150-300ms.
|
|
78
|
+
- **UI banned list + Pre-Flight checklist**: match-and-refuse blacklist for AI slop (default Inter/Roboto, AI purple-blue gradients, gradient text, cookie-cutter card grids, em-dash, cream/beige body backgrounds, nested cards) plus a delivery-gate self-check matrix.
|
|
79
|
+
|
|
70
80
|
### 🧠 Code Graph Bridge (GitNexus)
|
|
71
81
|
|
|
72
82
|
- `code_insight` bridges GitNexus by default for query/context/impact analysis
|
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 } 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;
|
|
@@ -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":
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { afterEach, describe, expect, test, vi } from 'vitest';
|
|
2
|
+
import { renderMemoryGuideSection, shouldShowSourceInSearch, } from '../memory-orchestration.js';
|
|
3
|
+
afterEach(() => {
|
|
4
|
+
vi.unstubAllEnvs();
|
|
5
|
+
});
|
|
6
|
+
describe('memory-orchestration', () => {
|
|
7
|
+
test('does not show source path by default', () => {
|
|
8
|
+
vi.stubEnv('MEMORY_SEARCH_SHOW_SOURCE', '');
|
|
9
|
+
vi.stubEnv('MEMORY_REPO_ID', '');
|
|
10
|
+
const section = renderMemoryGuideSection({
|
|
11
|
+
enabled: true,
|
|
12
|
+
available: true,
|
|
13
|
+
degraded: false,
|
|
14
|
+
query: '404 submit',
|
|
15
|
+
results: [
|
|
16
|
+
{
|
|
17
|
+
id: '1',
|
|
18
|
+
score: 0.91,
|
|
19
|
+
name: 'purchase-create-submit-404',
|
|
20
|
+
type: 'bugfix',
|
|
21
|
+
description: 'desc',
|
|
22
|
+
summary: 'summary',
|
|
23
|
+
content: '',
|
|
24
|
+
tags: ['bugfix'],
|
|
25
|
+
sourceProject: 'zhixing/gongyingshang',
|
|
26
|
+
sourcePath: 'admin-api/app.js',
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
assetsById: {},
|
|
30
|
+
});
|
|
31
|
+
expect(section).toContain('purchase-create-submit-404');
|
|
32
|
+
expect(section).not.toContain('admin-api/app.js');
|
|
33
|
+
expect(section).not.toContain('来源:');
|
|
34
|
+
});
|
|
35
|
+
test('shows source path when MEMORY_REPO_ID matches', () => {
|
|
36
|
+
vi.stubEnv('MEMORY_SEARCH_SHOW_SOURCE', '');
|
|
37
|
+
vi.stubEnv('MEMORY_REPO_ID', 'zhixing/gongyingshang');
|
|
38
|
+
expect(shouldShowSourceInSearch({
|
|
39
|
+
id: '1',
|
|
40
|
+
score: 0.9,
|
|
41
|
+
name: 'x',
|
|
42
|
+
type: 'bugfix',
|
|
43
|
+
description: '',
|
|
44
|
+
summary: '',
|
|
45
|
+
content: '',
|
|
46
|
+
tags: [],
|
|
47
|
+
sourceProject: 'zhixing/gongyingshang',
|
|
48
|
+
sourcePath: 'admin-api/app.js',
|
|
49
|
+
})).toBe(true);
|
|
50
|
+
expect(shouldShowSourceInSearch({
|
|
51
|
+
id: '2',
|
|
52
|
+
score: 0.9,
|
|
53
|
+
name: 'y',
|
|
54
|
+
type: 'bugfix',
|
|
55
|
+
description: '',
|
|
56
|
+
summary: '',
|
|
57
|
+
content: '',
|
|
58
|
+
tags: [],
|
|
59
|
+
sourceProject: 'other/repo',
|
|
60
|
+
sourcePath: 'src/index.ts',
|
|
61
|
+
})).toBe(false);
|
|
62
|
+
});
|
|
63
|
+
test('MEMORY_SEARCH_SHOW_SOURCE=true always shows source', () => {
|
|
64
|
+
vi.stubEnv('MEMORY_SEARCH_SHOW_SOURCE', 'true');
|
|
65
|
+
vi.stubEnv('MEMORY_REPO_ID', '');
|
|
66
|
+
const section = renderMemoryGuideSection({
|
|
67
|
+
enabled: true,
|
|
68
|
+
available: true,
|
|
69
|
+
degraded: false,
|
|
70
|
+
query: 'q',
|
|
71
|
+
results: [
|
|
72
|
+
{
|
|
73
|
+
id: '1',
|
|
74
|
+
score: 0.8,
|
|
75
|
+
name: 'n',
|
|
76
|
+
type: 'bugfix',
|
|
77
|
+
description: '',
|
|
78
|
+
summary: 's',
|
|
79
|
+
content: '',
|
|
80
|
+
tags: [],
|
|
81
|
+
sourcePath: 'admin-api/app.js',
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
assetsById: {},
|
|
85
|
+
});
|
|
86
|
+
expect(section).toContain('来源: admin-api/app.js');
|
|
87
|
+
});
|
|
88
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest';
|
|
2
|
+
import { normalizeMemoryPayload, payloadToMemoryFields } from '../memory-payload.js';
|
|
3
|
+
describe('memory-payload', () => {
|
|
4
|
+
test('maps legacy kind/title/source fields to current schema', () => {
|
|
5
|
+
const normalized = normalizeMemoryPayload({
|
|
6
|
+
kind: 'extracted_pattern',
|
|
7
|
+
title: 'Feishu submit success but sync_failed',
|
|
8
|
+
source: 'scan_and_extract_patterns-fallback',
|
|
9
|
+
content: 'Pattern: proxy mismatch',
|
|
10
|
+
tags: ['feishu', 'pattern'],
|
|
11
|
+
created_at: '2026-05-25T11:20:06.705Z',
|
|
12
|
+
});
|
|
13
|
+
expect(normalized.name).toBe('Feishu submit success but sync_failed');
|
|
14
|
+
expect(normalized.type).toBe('pattern');
|
|
15
|
+
expect(normalized.summary).toContain('Pattern');
|
|
16
|
+
expect(normalized.createdAt).toBe('2026-05-25T11:20:06.705Z');
|
|
17
|
+
});
|
|
18
|
+
test('payloadToMemoryFields preserves standard asset fields', () => {
|
|
19
|
+
const fields = payloadToMemoryFields({
|
|
20
|
+
id: 'asset-1',
|
|
21
|
+
name: 'purchase-create-submit-404',
|
|
22
|
+
type: 'bugfix',
|
|
23
|
+
description: '送审 404',
|
|
24
|
+
summary: 'res.data.purchase.id',
|
|
25
|
+
content: '【现象】404',
|
|
26
|
+
tags: ['bugfix'],
|
|
27
|
+
confidence: 0.95,
|
|
28
|
+
createdAt: '2026-05-27T04:28:04.684Z',
|
|
29
|
+
updatedAt: '2026-05-27T04:28:04.684Z',
|
|
30
|
+
});
|
|
31
|
+
expect(fields.name).toBe('purchase-create-submit-404');
|
|
32
|
+
expect(fields.type).toBe('bugfix');
|
|
33
|
+
expect(fields.tags).toEqual(['bugfix']);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { describe, test, expect } from 'vitest';
|
|
2
|
+
import { CODE_LIMITS, BANNED_CODE_PATTERNS, UI_HARD_RULES, UI_BANNED_LIST, renderCodeLimits, renderBannedPatterns, renderUiHardRules, renderUiBannedList, renderPreFlightChecklist, } from '../quality-constraints.js';
|
|
3
|
+
describe('quality-constraints(质量约束单一真相源)', () => {
|
|
4
|
+
test('CODE_LIMITS 数值符合预期(单文件 500 / 函数 50)', () => {
|
|
5
|
+
expect(CODE_LIMITS.maxFileLines).toBe(500);
|
|
6
|
+
expect(CODE_LIMITS.maxFunctionLines).toBe(50);
|
|
7
|
+
expect(CODE_LIMITS.maxNestingDepth).toBe(4);
|
|
8
|
+
expect(CODE_LIMITS.maxParameters).toBe(3);
|
|
9
|
+
});
|
|
10
|
+
test('黑名单与硬约束清单非空', () => {
|
|
11
|
+
expect(BANNED_CODE_PATTERNS.length).toBeGreaterThan(0);
|
|
12
|
+
expect(UI_HARD_RULES.length).toBeGreaterThan(0);
|
|
13
|
+
expect(UI_BANNED_LIST.length).toBeGreaterThan(0);
|
|
14
|
+
});
|
|
15
|
+
test('代码完整性黑名单含关键占位模式', () => {
|
|
16
|
+
expect(BANNED_CODE_PATTERNS).toContain('// TODO');
|
|
17
|
+
expect(BANNED_CODE_PATTERNS).toContain('// ...');
|
|
18
|
+
});
|
|
19
|
+
test('UI 黑名单含 em-dash 零容忍与 AI 紫蓝渐变', () => {
|
|
20
|
+
expect(UI_BANNED_LIST.some((b) => b.includes('em-dash'))).toBe(true);
|
|
21
|
+
expect(UI_BANNED_LIST.some((b) => b.includes('紫蓝渐变'))).toBe(true);
|
|
22
|
+
});
|
|
23
|
+
test('renderCodeLimits 输出含 500 行红线', () => {
|
|
24
|
+
const out = renderCodeLimits();
|
|
25
|
+
expect(out).toContain('500');
|
|
26
|
+
expect(out).toContain('HIGH');
|
|
27
|
+
});
|
|
28
|
+
test('renderBannedPatterns 输出含 CRITICAL 与二元规则', () => {
|
|
29
|
+
const out = renderBannedPatterns();
|
|
30
|
+
expect(out).toContain('CRITICAL');
|
|
31
|
+
expect(out).toContain('二元');
|
|
32
|
+
});
|
|
33
|
+
test('renderUiHardRules 含 4pt 间距阶梯与对比度阈值', () => {
|
|
34
|
+
const out = renderUiHardRules();
|
|
35
|
+
expect(out).toContain('4, 8, 12, 16, 24, 32, 48, 64, 96');
|
|
36
|
+
expect(out).toContain('4.5:1');
|
|
37
|
+
});
|
|
38
|
+
test('renderUiBannedList 输出每条以 ❌ 标记', () => {
|
|
39
|
+
const out = renderUiBannedList();
|
|
40
|
+
expect(out).toContain('❌');
|
|
41
|
+
});
|
|
42
|
+
test('renderPreFlightChecklist 含 Scope-lock 与完整性勾选项', () => {
|
|
43
|
+
const out = renderPreFlightChecklist();
|
|
44
|
+
expect(out).toContain('Pre-Flight');
|
|
45
|
+
expect(out).toContain('Scope-lock');
|
|
46
|
+
expect(out).toContain('占位符');
|
|
47
|
+
});
|
|
48
|
+
// 防回归:注入下游 prompt 的渲染输出自身不得含 em-dash,否则与「em-dash 零容忍」自相矛盾
|
|
49
|
+
test('代码类渲染输出不含 em-dash(避免规则自相矛盾)', () => {
|
|
50
|
+
expect(renderCodeLimits()).not.toContain('—');
|
|
51
|
+
expect(renderBannedPatterns()).not.toContain('—');
|
|
52
|
+
expect(renderPreFlightChecklist()).not.toContain('—');
|
|
53
|
+
});
|
|
54
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,147 @@
|
|
|
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
|
+
## 交付物清单(Scope-lock)
|
|
60
|
+
- 预计新建文件数: 1 个
|
|
61
|
+
- 预计修改文件数: 0 个
|
|
62
|
+
|
|
63
|
+
## 任务列表
|
|
64
|
+
### 阶段 2: 核心实现
|
|
65
|
+
- [ ] 2.1 实现登录接口 POST /api/login,校验邮箱并返回 JWT
|
|
66
|
+
- 证据块: 先读 src/router.ts:1,确认路由注册方式
|
|
67
|
+
- _需求: FR-1_ | _设计: API 设计_
|
|
68
|
+
|
|
69
|
+
## 检查点
|
|
70
|
+
- [ ] 阶段 2 完成后:登录可用
|
|
71
|
+
|
|
72
|
+
## 需求覆盖矩阵
|
|
73
|
+
| 需求 ID | 设计章节 | 任务编号 | 状态 |
|
|
74
|
+
| FR-1 | API 设计 | 2.1 | 未开始 |
|
|
75
|
+
|
|
76
|
+
## 文件变更清单
|
|
77
|
+
| 文件 | 操作 | 说明 |
|
|
78
|
+
| src/login.ts | 新建 | 登录接口 |
|
|
79
|
+
`;
|
|
80
|
+
describe('spec-validator(P1 填写后校验)', () => {
|
|
81
|
+
test('完整规格通过校验', () => {
|
|
82
|
+
const r = validateSpecDocuments({ requirements: goodReq, design: goodDesign, tasks: goodTasks });
|
|
83
|
+
expect(r.passed).toBe(true);
|
|
84
|
+
expect(r.errorCount).toBe(0);
|
|
85
|
+
expect(r.frIds).toContain('FR-1');
|
|
86
|
+
});
|
|
87
|
+
test('残留 [填写:] 占位 → 未通过', () => {
|
|
88
|
+
const r = validateSpecDocuments({ requirements: goodReq + '\n- [填写:补充]', design: goodDesign, tasks: goodTasks });
|
|
89
|
+
expect(r.passed).toBe(false);
|
|
90
|
+
expect(r.issues.some((i) => i.file === 'requirements' && i.code === 'placeholder')).toBe(true);
|
|
91
|
+
});
|
|
92
|
+
test('缺少 design 文件 → 未通过', () => {
|
|
93
|
+
const r = validateSpecDocuments({ requirements: goodReq, design: null, tasks: goodTasks });
|
|
94
|
+
expect(r.passed).toBe(false);
|
|
95
|
+
expect(r.issues.some((i) => i.file === 'design' && i.code === 'missing_file')).toBe(true);
|
|
96
|
+
});
|
|
97
|
+
test('无 EARS 验收标准 → 未通过', () => {
|
|
98
|
+
const r = validateSpecDocuments({
|
|
99
|
+
requirements: goodReq.replace('1. WHEN 提交正确凭证 THEN 系统 SHALL 登录成功', '1. 能登录'),
|
|
100
|
+
design: goodDesign,
|
|
101
|
+
tasks: goodTasks,
|
|
102
|
+
});
|
|
103
|
+
expect(r.passed).toBe(false);
|
|
104
|
+
expect(r.issues.some((i) => i.code === 'no_acceptance')).toBe(true);
|
|
105
|
+
});
|
|
106
|
+
test('FR 未进 tasks 覆盖矩阵 → 跨文档未覆盖', () => {
|
|
107
|
+
const reqTwoFr = goodReq.replace('## 非功能需求', `### FR-2: 登出
|
|
108
|
+
**优先级:** Should
|
|
109
|
+
**用户故事:** 作为用户,我想登出。
|
|
110
|
+
#### 验收标准(EARS)
|
|
111
|
+
1. WHEN 点击登出 THEN 系统 SHALL 退出登录
|
|
112
|
+
|
|
113
|
+
## 非功能需求`);
|
|
114
|
+
const r = validateSpecDocuments({ requirements: reqTwoFr, design: goodDesign, tasks: goodTasks });
|
|
115
|
+
expect(r.passed).toBe(false);
|
|
116
|
+
expect(r.issues.some((i) => i.code === 'uncovered_fr')).toBe(true);
|
|
117
|
+
});
|
|
118
|
+
test('extractFrIds 去重并保序', () => {
|
|
119
|
+
expect(extractFrIds('FR-1 写了 FR-1 又写 FR-2')).toEqual(['FR-1', 'FR-2']);
|
|
120
|
+
});
|
|
121
|
+
test('tasks 缺少「交付物清单」章节 → 未通过', () => {
|
|
122
|
+
const tasksNoScope = goodTasks.replace(/## 交付物清单(Scope-lock)[\s\S]*?\n\n/, '');
|
|
123
|
+
// 防 fixture 漂移导致替换失效造成假通过
|
|
124
|
+
expect(tasksNoScope).not.toContain('交付物清单');
|
|
125
|
+
const r = validateSpecDocuments({ requirements: goodReq, design: goodDesign, tasks: tasksNoScope });
|
|
126
|
+
expect(r.passed).toBe(false);
|
|
127
|
+
expect(r.issues.some((i) => i.file === 'tasks' && i.code === 'missing_section')).toBe(true);
|
|
128
|
+
});
|
|
129
|
+
test('任务无「证据块」→ thin_task 提醒(warning,不阻断)', () => {
|
|
130
|
+
const tasksThin = goodTasks.replace(' - 证据块: 先读 src/router.ts:1,确认路由注册方式\n', '');
|
|
131
|
+
expect(tasksThin).not.toBe(goodTasks);
|
|
132
|
+
const r = validateSpecDocuments({ requirements: goodReq, design: goodDesign, tasks: tasksThin });
|
|
133
|
+
expect(r.issues.some((i) => i.code === 'thin_task')).toBe(true);
|
|
134
|
+
expect(r.warningCount).toBeGreaterThan(0);
|
|
135
|
+
// 核心语义:thin_task 是 warning,不应阻断 passed
|
|
136
|
+
expect(r.passed).toBe(true);
|
|
137
|
+
});
|
|
138
|
+
test('裸 [填写] 占位(无冒号)也应被检出', () => {
|
|
139
|
+
const r = validateSpecDocuments({
|
|
140
|
+
requirements: goodReq + '\n- 备注: [填写]',
|
|
141
|
+
design: goodDesign,
|
|
142
|
+
tasks: goodTasks,
|
|
143
|
+
});
|
|
144
|
+
expect(r.passed).toBe(false);
|
|
145
|
+
expect(r.issues.some((i) => i.file === 'requirements' && i.code === 'placeholder')).toBe(true);
|
|
146
|
+
});
|
|
147
|
+
});
|
|
@@ -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) {
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export interface CursorConversationSummary {
|
|
2
|
+
composerId: string;
|
|
3
|
+
name: string;
|
|
4
|
+
createdAt?: number;
|
|
5
|
+
lastUpdatedAt?: number;
|
|
6
|
+
workspaceId?: string;
|
|
7
|
+
workspacePath?: string;
|
|
8
|
+
mode?: string;
|
|
9
|
+
contextUsagePercent?: number;
|
|
10
|
+
subtitle?: string;
|
|
11
|
+
isArchived?: boolean;
|
|
12
|
+
source: 'composerHeaders';
|
|
13
|
+
}
|
|
14
|
+
export interface CursorConversationMessage {
|
|
15
|
+
bubbleId: string;
|
|
16
|
+
type: number;
|
|
17
|
+
text: string;
|
|
18
|
+
createdAt?: string;
|
|
19
|
+
requestId?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface CursorConversationDetail {
|
|
22
|
+
composerId: string;
|
|
23
|
+
messages: CursorConversationMessage[];
|
|
24
|
+
}
|
|
25
|
+
export interface CursorHistorySearchResult {
|
|
26
|
+
composerId: string;
|
|
27
|
+
conversationName: string;
|
|
28
|
+
bubbleId: string;
|
|
29
|
+
type: number;
|
|
30
|
+
text: string;
|
|
31
|
+
createdAt?: string;
|
|
32
|
+
requestId?: string;
|
|
33
|
+
}
|
|
34
|
+
export declare class CursorHistoryClient {
|
|
35
|
+
private withDatabase;
|
|
36
|
+
private loadConversationIndex;
|
|
37
|
+
listConversations(params?: {
|
|
38
|
+
titleQuery?: string;
|
|
39
|
+
workspaceQuery?: string;
|
|
40
|
+
includeArchived?: boolean;
|
|
41
|
+
limit?: number;
|
|
42
|
+
}): Promise<CursorConversationSummary[]>;
|
|
43
|
+
searchHistory(params: {
|
|
44
|
+
query: string;
|
|
45
|
+
composerId?: string;
|
|
46
|
+
limit?: number;
|
|
47
|
+
}): Promise<CursorHistorySearchResult[]>;
|
|
48
|
+
readConversation(params: {
|
|
49
|
+
composerId: string;
|
|
50
|
+
limit?: number;
|
|
51
|
+
includeEmpty?: boolean;
|
|
52
|
+
}): Promise<CursorConversationDetail>;
|
|
53
|
+
}
|
|
54
|
+
export declare function createCursorHistoryClient(): CursorHistoryClient;
|