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.
- package/README.md +755 -779
- package/build/index.js +42 -41
- package/build/lib/__tests__/spec-validator.unit.test.js +115 -0
- package/build/lib/agents-md-template.js +32 -32
- package/build/lib/memory-orchestration.js +29 -8
- package/build/lib/skill-bridge.js +12 -12
- package/build/lib/spec-validator.d.ts +36 -0
- package/build/lib/spec-validator.js +103 -0
- package/build/lib/template-loader.js +149 -47
- 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/index.d.ts +4 -0
- package/build/resources/index.js +4 -0
- package/build/resources/tool-params-guide.d.ts +571 -0
- package/build/resources/tool-params-guide.js +488 -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 -22
- package/build/schemas/memory-tools.d.ts +0 -22
- package/build/schemas/memory-tools.js +0 -14
- package/build/schemas/project-tools.d.ts +22 -0
- package/build/schemas/project-tools.js +23 -0
- package/build/tools/analyze_project.d.ts +1 -0
- package/build/tools/analyze_project.js +527 -0
- package/build/tools/check_deps.d.ts +13 -0
- package/build/tools/check_deps.js +204 -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/convert.d.ts +13 -0
- package/build/tools/convert.js +599 -0
- package/build/tools/css_order.d.ts +13 -0
- package/build/tools/css_order.js +81 -0
- package/build/tools/debug.d.ts +13 -0
- package/build/tools/debug.js +131 -0
- package/build/tools/design2code.d.ts +20 -0
- package/build/tools/design2code.js +426 -0
- package/build/tools/detect_shell.d.ts +6 -0
- package/build/tools/detect_shell.js +151 -0
- package/build/tools/explain.d.ts +13 -0
- package/build/tools/explain.js +390 -0
- package/build/tools/fix.d.ts +13 -0
- package/build/tools/fix.js +303 -0
- package/build/tools/fix_bug.js +161 -161
- package/build/tools/gen_mock.d.ts +22 -0
- package/build/tools/gen_mock.js +269 -0
- package/build/tools/gen_skill.d.ts +13 -0
- package/build/tools/gen_skill.js +560 -0
- package/build/tools/genapi.d.ts +13 -0
- package/build/tools/genapi.js +174 -0
- package/build/tools/genchangelog.d.ts +13 -0
- package/build/tools/genchangelog.js +250 -0
- package/build/tools/gencommit.js +60 -60
- package/build/tools/gendoc.d.ts +13 -0
- package/build/tools/gendoc.js +232 -0
- package/build/tools/genpr.d.ts +13 -0
- package/build/tools/genpr.js +194 -0
- package/build/tools/genreadme.d.ts +13 -0
- package/build/tools/genreadme.js +626 -0
- package/build/tools/gensql.d.ts +13 -0
- package/build/tools/gensql.js +320 -0
- package/build/tools/genui.d.ts +13 -0
- package/build/tools/genui.js +803 -0
- package/build/tools/index.d.ts +1 -1
- package/build/tools/index.js +1 -1
- package/build/tools/init_component_catalog.d.ts +22 -0
- package/build/tools/init_component_catalog.js +809 -0
- package/build/tools/init_project_context.js +432 -432
- package/build/tools/init_setting.d.ts +13 -0
- package/build/tools/init_setting.js +47 -0
- package/build/tools/perf.d.ts +13 -0
- package/build/tools/perf.js +409 -0
- package/build/tools/render_ui.d.ts +22 -0
- package/build/tools/render_ui.js +384 -0
- package/build/tools/resolve_conflict.d.ts +13 -0
- package/build/tools/resolve_conflict.js +349 -0
- package/build/tools/security_scan.d.ts +22 -0
- package/build/tools/security_scan.js +323 -0
- package/build/tools/split.d.ts +13 -0
- package/build/tools/split.js +599 -0
- package/build/tools/start_api.d.ts +13 -0
- package/build/tools/start_api.js +193 -0
- package/build/tools/start_bugfix.js +254 -243
- package/build/tools/start_doc.d.ts +13 -0
- package/build/tools/start_doc.js +207 -0
- package/build/tools/start_feature.js +162 -127
- package/build/tools/start_product.js +1 -1
- package/build/tools/start_refactor.d.ts +13 -0
- package/build/tools/start_refactor.js +188 -0
- package/build/tools/start_release.d.ts +13 -0
- package/build/tools/start_release.js +167 -0
- package/build/tools/start_review.d.ts +13 -0
- package/build/tools/start_review.js +175 -0
- package/build/tools/start_ui.js +426 -412
- package/build/tools/ui-ux-tools.js +290 -290
- package/build/utils/__tests__/vercel-guidelines-sync.unit.test.js +12 -12
- package/build/utils/themes-sync.js +8 -8
- package/package.json +81 -83
- package/build/lib/__tests__/memory-orchestration.unit.test.js +0 -88
- package/build/lib/__tests__/memory-payload.unit.test.js +0 -35
- package/build/lib/cursor-history-client.d.ts +0 -54
- package/build/lib/cursor-history-client.js +0 -240
- package/build/tools/__tests__/cursor-history.unit.test.js +0 -38
- package/build/tools/cursor_read_conversation.d.ts +0 -7
- package/build/tools/cursor_read_conversation.js +0 -36
- package/docs/.mcp-probe/layout.json +0 -11
- package/docs/CNAME +0 -1
- package/docs/assets/font/MaterialSymbolsOutlined.codepoints +0 -4102
- package/docs/assets/font/MaterialSymbolsOutlined.ttf +0 -0
- package/docs/assets/font/noto-sans-sc-400.ttf +0 -0
- package/docs/assets/font/noto-sans-sc-700.ttf +0 -0
- package/docs/assets/font/noto-sans-sc-900.ttf +0 -0
- package/docs/assets/js/i18n.js +0 -375
- package/docs/assets/js/tailwind.js +0 -83
- package/docs/assets/logo-zh.png +0 -0
- package/docs/assets/logo.png +0 -0
- package/docs/data/tools.js +0 -523
- package/docs/i18n/all-tools/en.json +0 -190
- package/docs/i18n/all-tools/ja.json +0 -171
- package/docs/i18n/all-tools/ko.json +0 -171
- package/docs/i18n/all-tools/zh-CN.json +0 -190
- package/docs/i18n/en.json +0 -626
- package/docs/i18n/ja.json +0 -602
- package/docs/i18n/ko.json +0 -602
- package/docs/i18n/zh-CN.json +0 -626
- package/docs/index.html +0 -327
- package/docs/memory-local-setup.md +0 -315
- package/docs/memory-local-setup.zh-CN.md +0 -283
- package/docs/pages/all-tools.html +0 -515
- package/docs/pages/examples.html +0 -717
- package/docs/pages/getting-started.html +0 -964
- package/docs/pages/migration.html +0 -308
- package/docs/specs/user-auth/design.md +0 -82
- package/docs/specs/user-auth/requirements.md +0 -52
- package/docs/specs/user-auth/tasks.md +0 -55
- /package/build/lib/__tests__/{memory-orchestration.unit.test.d.ts → spec-validator.unit.test.d.ts} +0 -0
- /package/build/{lib/__tests__/memory-payload.unit.test.d.ts → utils/design-docs-generator.d.ts} +0 -0
- /package/build/{tools/__tests__/cursor-history.unit.test.d.ts → utils/design-docs-generator.js} +0 -0
|
@@ -4,18 +4,18 @@ import * as fs from 'fs';
|
|
|
4
4
|
import * as path from 'path';
|
|
5
5
|
describe('vercel-guidelines-sync', () => {
|
|
6
6
|
it('parses MUST/SHOULD/NEVER rules from markdown', () => {
|
|
7
|
-
const sample = `# Title
|
|
8
|
-
|
|
9
|
-
## Interactions
|
|
10
|
-
|
|
11
|
-
### Keyboard
|
|
12
|
-
|
|
13
|
-
- MUST: Visible focus rings
|
|
14
|
-
- NEVER: outline: none without replacement
|
|
15
|
-
|
|
16
|
-
## Animation
|
|
17
|
-
|
|
18
|
-
- SHOULD: Prefer CSS animations
|
|
7
|
+
const sample = `# Title
|
|
8
|
+
|
|
9
|
+
## Interactions
|
|
10
|
+
|
|
11
|
+
### Keyboard
|
|
12
|
+
|
|
13
|
+
- MUST: Visible focus rings
|
|
14
|
+
- NEVER: outline: none without replacement
|
|
15
|
+
|
|
16
|
+
## Animation
|
|
17
|
+
|
|
18
|
+
- SHOULD: Prefer CSS animations
|
|
19
19
|
`;
|
|
20
20
|
const records = parseVercelGuidelinesMarkdown(sample);
|
|
21
21
|
expect(records).toHaveLength(3);
|
|
@@ -66,14 +66,14 @@ function renderGlobalsCss(name, light, dark) {
|
|
|
66
66
|
const toBlock = (vars) => Object.entries(vars)
|
|
67
67
|
.map(([key, value]) => ` --${key}: ${value};`)
|
|
68
68
|
.join('\n');
|
|
69
|
-
return `@layer base {
|
|
70
|
-
/* Theme: ${name} — paste into globals.css */
|
|
71
|
-
:root {
|
|
72
|
-
${toBlock(light)}
|
|
73
|
-
}
|
|
74
|
-
.dark {
|
|
75
|
-
${toBlock(dark)}
|
|
76
|
-
}
|
|
69
|
+
return `@layer base {
|
|
70
|
+
/* Theme: ${name} — paste into globals.css */
|
|
71
|
+
:root {
|
|
72
|
+
${toBlock(light)}
|
|
73
|
+
}
|
|
74
|
+
.dark {
|
|
75
|
+
${toBlock(dark)}
|
|
76
|
+
}
|
|
77
77
|
}`;
|
|
78
78
|
}
|
|
79
79
|
function buildTheme(input) {
|
package/package.json
CHANGED
|
@@ -1,83 +1,81 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "mcp-probe-kit",
|
|
3
|
-
"version": "3.0
|
|
4
|
-
"description": "AI-Powered Development Toolkit - MCP Server with 27 tools covering code quality, development efficiency, project management, and UI/UX design. Features: Structured Output, Workflow Orchestration, UI/UX Pro Max, and Requirements Interview.",
|
|
5
|
-
"mcpName": "io.github.mybolide/mcp-probe-kit",
|
|
6
|
-
"type": "module",
|
|
7
|
-
"main": "build/index.js",
|
|
8
|
-
"bin": {
|
|
9
|
-
"mcp-probe-kit": "build/index.js"
|
|
10
|
-
},
|
|
11
|
-
"scripts": {
|
|
12
|
-
"build": "tsc",
|
|
13
|
-
"postbuild": "node scripts/copy-ui-embedded-data.mjs",
|
|
14
|
-
"watch": "tsc --watch",
|
|
15
|
-
"dev": "tsc && node build/index.js",
|
|
16
|
-
"test": "vitest --run",
|
|
17
|
-
"test:watch": "vitest",
|
|
18
|
-
"test:ui": "vitest --ui",
|
|
19
|
-
"inspector": "npx @modelcontextprotocol/inspector node build/index.js",
|
|
20
|
-
"prepare": "npm run build",
|
|
21
|
-
"prepublishOnly": "npm run build",
|
|
22
|
-
"sync-ui-data": "tsx scripts/sync-ui-data.ts",
|
|
23
|
-
"prebuild": "npm run sync-ui-data",
|
|
24
|
-
"docs:serve": "npx http-server docs -p 8080 -o"
|
|
25
|
-
},
|
|
26
|
-
"keywords": [
|
|
27
|
-
"mcp",
|
|
28
|
-
"model-context-protocol",
|
|
29
|
-
"cursor",
|
|
30
|
-
"ai-tools",
|
|
31
|
-
"development-tools",
|
|
32
|
-
"code-quality",
|
|
33
|
-
"code-review",
|
|
34
|
-
"refactor",
|
|
35
|
-
"debugging",
|
|
36
|
-
"sql-generator",
|
|
37
|
-
"ui-generator",
|
|
38
|
-
"react",
|
|
39
|
-
"vue",
|
|
40
|
-
"typescript",
|
|
41
|
-
"git-tools",
|
|
42
|
-
"ai-assistant"
|
|
43
|
-
],
|
|
44
|
-
"author": {
|
|
45
|
-
"name": "小墨 (Kyle)",
|
|
46
|
-
"url": "https://www.bytezonex.com/"
|
|
47
|
-
},
|
|
48
|
-
"license": "MIT",
|
|
49
|
-
"repository": {
|
|
50
|
-
"type": "git",
|
|
51
|
-
"url": "git+https://github.com/mybolide/mcp-probe-kit.git"
|
|
52
|
-
},
|
|
53
|
-
"bugs": {
|
|
54
|
-
"url": "https://github.com/mybolide/mcp-probe-kit/issues"
|
|
55
|
-
},
|
|
56
|
-
"homepage": "https://mcp-probe-kit.bytezonex.com",
|
|
57
|
-
"engines": {
|
|
58
|
-
"node": ">=18.0.0"
|
|
59
|
-
},
|
|
60
|
-
"files": [
|
|
61
|
-
"build",
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
"@types/
|
|
75
|
-
"@
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
83
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "mcp-probe-kit",
|
|
3
|
+
"version": "3.2.0",
|
|
4
|
+
"description": "AI-Powered Development Toolkit - MCP Server with 27 tools covering code quality, development efficiency, project management, and UI/UX design. Features: Structured Output, Workflow Orchestration, UI/UX Pro Max, and Requirements Interview.",
|
|
5
|
+
"mcpName": "io.github.mybolide/mcp-probe-kit",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "build/index.js",
|
|
8
|
+
"bin": {
|
|
9
|
+
"mcp-probe-kit": "build/index.js"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"postbuild": "node scripts/copy-ui-embedded-data.mjs",
|
|
14
|
+
"watch": "tsc --watch",
|
|
15
|
+
"dev": "tsc && node build/index.js",
|
|
16
|
+
"test": "vitest --run",
|
|
17
|
+
"test:watch": "vitest",
|
|
18
|
+
"test:ui": "vitest --ui",
|
|
19
|
+
"inspector": "npx @modelcontextprotocol/inspector node build/index.js",
|
|
20
|
+
"prepare": "npm run build",
|
|
21
|
+
"prepublishOnly": "npm run build",
|
|
22
|
+
"sync-ui-data": "tsx scripts/sync-ui-data.ts",
|
|
23
|
+
"prebuild": "npm run sync-ui-data",
|
|
24
|
+
"docs:serve": "npx http-server docs -p 8080 -o"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"mcp",
|
|
28
|
+
"model-context-protocol",
|
|
29
|
+
"cursor",
|
|
30
|
+
"ai-tools",
|
|
31
|
+
"development-tools",
|
|
32
|
+
"code-quality",
|
|
33
|
+
"code-review",
|
|
34
|
+
"refactor",
|
|
35
|
+
"debugging",
|
|
36
|
+
"sql-generator",
|
|
37
|
+
"ui-generator",
|
|
38
|
+
"react",
|
|
39
|
+
"vue",
|
|
40
|
+
"typescript",
|
|
41
|
+
"git-tools",
|
|
42
|
+
"ai-assistant"
|
|
43
|
+
],
|
|
44
|
+
"author": {
|
|
45
|
+
"name": "小墨 (Kyle)",
|
|
46
|
+
"url": "https://www.bytezonex.com/"
|
|
47
|
+
},
|
|
48
|
+
"license": "MIT",
|
|
49
|
+
"repository": {
|
|
50
|
+
"type": "git",
|
|
51
|
+
"url": "git+https://github.com/mybolide/mcp-probe-kit.git"
|
|
52
|
+
},
|
|
53
|
+
"bugs": {
|
|
54
|
+
"url": "https://github.com/mybolide/mcp-probe-kit/issues"
|
|
55
|
+
},
|
|
56
|
+
"homepage": "https://mcp-probe-kit.bytezonex.com",
|
|
57
|
+
"engines": {
|
|
58
|
+
"node": ">=18.0.0"
|
|
59
|
+
},
|
|
60
|
+
"files": [
|
|
61
|
+
"build",
|
|
62
|
+
"README.md",
|
|
63
|
+
"LICENSE"
|
|
64
|
+
],
|
|
65
|
+
"dependencies": {
|
|
66
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
67
|
+
"cross-spawn": "^7.0.6",
|
|
68
|
+
"csv-parse": "^6.1.0",
|
|
69
|
+
"tar": "^7.5.6"
|
|
70
|
+
},
|
|
71
|
+
"devDependencies": {
|
|
72
|
+
"@types/cross-spawn": "^6.0.6",
|
|
73
|
+
"@types/node": "^20.0.0",
|
|
74
|
+
"@types/tar": "^6.1.13",
|
|
75
|
+
"@vitest/ui": "^4.0.18",
|
|
76
|
+
"fast-check": "^4.5.3",
|
|
77
|
+
"tsx": "^4.21.0",
|
|
78
|
+
"typescript": "^5.3.0",
|
|
79
|
+
"vitest": "^4.0.18"
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -1,88 +0,0 @@
|
|
|
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
|
-
});
|
|
@@ -1,35 +0,0 @@
|
|
|
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
|
-
});
|
|
@@ -1,54 +0,0 @@
|
|
|
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;
|
|
@@ -1,240 +0,0 @@
|
|
|
1
|
-
import * as fs from 'node:fs';
|
|
2
|
-
import * as os from 'node:os';
|
|
3
|
-
import * as path from 'node:path';
|
|
4
|
-
import sqlite3 from 'sqlite3';
|
|
5
|
-
import { createToolError } from '../utils/error-handler.js';
|
|
6
|
-
function parseJson(value) {
|
|
7
|
-
if (!value) {
|
|
8
|
-
return null;
|
|
9
|
-
}
|
|
10
|
-
try {
|
|
11
|
-
return JSON.parse(value);
|
|
12
|
-
}
|
|
13
|
-
catch {
|
|
14
|
-
return null;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
function fileExists(filePath) {
|
|
18
|
-
try {
|
|
19
|
-
return fs.existsSync(filePath) && fs.statSync(filePath).isFile();
|
|
20
|
-
}
|
|
21
|
-
catch {
|
|
22
|
-
return false;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
function resolveCursorGlobalDbPath() {
|
|
26
|
-
const appData = process.env.APPDATA?.trim();
|
|
27
|
-
const homeDir = os.homedir();
|
|
28
|
-
const candidates = [
|
|
29
|
-
appData ? path.join(appData, 'Cursor', 'User', 'globalStorage', 'state.vscdb') : '',
|
|
30
|
-
path.join(homeDir, '.config', 'Cursor', 'User', 'globalStorage', 'state.vscdb'),
|
|
31
|
-
path.join(homeDir, 'Library', 'Application Support', 'Cursor', 'User', 'globalStorage', 'state.vscdb'),
|
|
32
|
-
].filter(Boolean);
|
|
33
|
-
const hit = candidates.find(fileExists);
|
|
34
|
-
if (!hit) {
|
|
35
|
-
throw createToolError('未找到 Cursor 全局状态库 state.vscdb', undefined, {
|
|
36
|
-
candidates,
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
return hit;
|
|
40
|
-
}
|
|
41
|
-
function openDatabase(dbPath) {
|
|
42
|
-
return new Promise((resolve, reject) => {
|
|
43
|
-
const db = new sqlite3.Database(dbPath, sqlite3.OPEN_READONLY, (error) => {
|
|
44
|
-
if (error) {
|
|
45
|
-
reject(createToolError(`打开 Cursor 数据库失败: ${error.message}`, error, { dbPath }));
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
resolve(db);
|
|
49
|
-
});
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
function closeDatabase(db) {
|
|
53
|
-
return new Promise((resolve, reject) => {
|
|
54
|
-
db.close((error) => {
|
|
55
|
-
if (error) {
|
|
56
|
-
reject(error);
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
resolve();
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
function allRows(db, sql, params = []) {
|
|
64
|
-
return new Promise((resolve, reject) => {
|
|
65
|
-
db.all(sql, params, (error, rows) => {
|
|
66
|
-
if (error) {
|
|
67
|
-
reject(error);
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
resolve((rows ?? []));
|
|
71
|
-
});
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
function getRow(db, sql, params = []) {
|
|
75
|
-
return new Promise((resolve, reject) => {
|
|
76
|
-
db.get(sql, params, (error, row) => {
|
|
77
|
-
if (error) {
|
|
78
|
-
reject(error);
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
resolve(row);
|
|
82
|
-
});
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
function asString(value) {
|
|
86
|
-
return typeof value === 'string' ? value : '';
|
|
87
|
-
}
|
|
88
|
-
function asNumber(value) {
|
|
89
|
-
return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
|
|
90
|
-
}
|
|
91
|
-
function asBoolean(value) {
|
|
92
|
-
return typeof value === 'boolean' ? value : undefined;
|
|
93
|
-
}
|
|
94
|
-
function extractConversationsFromHeaders(payload) {
|
|
95
|
-
const items = payload?.allComposers ?? [];
|
|
96
|
-
return items
|
|
97
|
-
.filter((item) => item?.type === 'head')
|
|
98
|
-
.map((item) => {
|
|
99
|
-
const workspace = item.workspaceIdentifier ?? {};
|
|
100
|
-
const uri = workspace.uri ?? {};
|
|
101
|
-
return {
|
|
102
|
-
composerId: asString(item.composerId),
|
|
103
|
-
name: asString(item.name),
|
|
104
|
-
createdAt: asNumber(item.createdAt),
|
|
105
|
-
lastUpdatedAt: asNumber(item.lastUpdatedAt),
|
|
106
|
-
workspaceId: asString(workspace.id) || undefined,
|
|
107
|
-
workspacePath: asString(uri.fsPath) || undefined,
|
|
108
|
-
mode: asString(item.unifiedMode) || undefined,
|
|
109
|
-
contextUsagePercent: asNumber(item.contextUsagePercent),
|
|
110
|
-
subtitle: asString(item.subtitle) || undefined,
|
|
111
|
-
isArchived: asBoolean(item.isArchived) ?? false,
|
|
112
|
-
source: 'composerHeaders',
|
|
113
|
-
};
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
function extractBubbleId(key) {
|
|
117
|
-
const parts = key.split(':');
|
|
118
|
-
return parts[parts.length - 1] || key;
|
|
119
|
-
}
|
|
120
|
-
function extractComposerId(key) {
|
|
121
|
-
const parts = key.split(':');
|
|
122
|
-
return parts.length >= 3 ? parts[1] || '' : '';
|
|
123
|
-
}
|
|
124
|
-
export class CursorHistoryClient {
|
|
125
|
-
async withDatabase(runner) {
|
|
126
|
-
const dbPath = resolveCursorGlobalDbPath();
|
|
127
|
-
const db = await openDatabase(dbPath);
|
|
128
|
-
try {
|
|
129
|
-
return await runner(db);
|
|
130
|
-
}
|
|
131
|
-
catch (error) {
|
|
132
|
-
if (error instanceof Error) {
|
|
133
|
-
throw createToolError(`读取 Cursor 历史失败: ${error.message}`, error, { dbPath });
|
|
134
|
-
}
|
|
135
|
-
throw error;
|
|
136
|
-
}
|
|
137
|
-
finally {
|
|
138
|
-
await closeDatabase(db).catch(() => undefined);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
async loadConversationIndex(db) {
|
|
142
|
-
const row = await getRow(db, "select key, value from ItemTable where key = ?", ['composer.composerHeaders']);
|
|
143
|
-
const payload = parseJson(row?.value);
|
|
144
|
-
return extractConversationsFromHeaders(payload);
|
|
145
|
-
}
|
|
146
|
-
async listConversations(params = {}) {
|
|
147
|
-
return this.withDatabase(async (db) => {
|
|
148
|
-
const titleQuery = (params.titleQuery ?? '').trim().toLowerCase();
|
|
149
|
-
const workspaceQuery = (params.workspaceQuery ?? '').trim().toLowerCase();
|
|
150
|
-
const includeArchived = params.includeArchived ?? false;
|
|
151
|
-
const limit = Math.max(1, Math.min(params.limit ?? 20, 200));
|
|
152
|
-
const conversations = await this.loadConversationIndex(db);
|
|
153
|
-
return conversations
|
|
154
|
-
.filter((item) => includeArchived || !item.isArchived)
|
|
155
|
-
.filter((item) => !titleQuery || item.name.toLowerCase().includes(titleQuery))
|
|
156
|
-
.filter((item) => !workspaceQuery || (item.workspacePath ?? '').toLowerCase().includes(workspaceQuery))
|
|
157
|
-
.sort((a, b) => (b.lastUpdatedAt ?? 0) - (a.lastUpdatedAt ?? 0))
|
|
158
|
-
.slice(0, limit);
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
async searchHistory(params) {
|
|
162
|
-
return this.withDatabase(async (db) => {
|
|
163
|
-
const query = (params.query ?? '').trim().toLowerCase();
|
|
164
|
-
if (!query) {
|
|
165
|
-
throw new Error('缺少 query');
|
|
166
|
-
}
|
|
167
|
-
const limit = Math.max(1, Math.min(params.limit ?? 20, 200));
|
|
168
|
-
const composerId = (params.composerId ?? '').trim();
|
|
169
|
-
const index = await this.loadConversationIndex(db);
|
|
170
|
-
const names = new Map(index.map((item) => [item.composerId, item.name]));
|
|
171
|
-
const rows = await allRows(db, 'select key, value from cursorDiskKV where key like ?', [composerId ? `bubbleId:${composerId}:%` : 'bubbleId:%']);
|
|
172
|
-
const matches = rows
|
|
173
|
-
.map((row) => {
|
|
174
|
-
const parsed = parseJson(row.value);
|
|
175
|
-
if (!parsed) {
|
|
176
|
-
return null;
|
|
177
|
-
}
|
|
178
|
-
const text = asString(parsed.text);
|
|
179
|
-
const requestId = asString(parsed.requestId);
|
|
180
|
-
const haystack = `${row.key}\n${text}\n${requestId}`.toLowerCase();
|
|
181
|
-
if (!haystack.includes(query)) {
|
|
182
|
-
return null;
|
|
183
|
-
}
|
|
184
|
-
const currentComposerId = extractComposerId(row.key);
|
|
185
|
-
return {
|
|
186
|
-
composerId: currentComposerId,
|
|
187
|
-
conversationName: names.get(currentComposerId) ?? '',
|
|
188
|
-
bubbleId: extractBubbleId(row.key),
|
|
189
|
-
type: asNumber(parsed.type) ?? 0,
|
|
190
|
-
text,
|
|
191
|
-
createdAt: asString(parsed.createdAt) || undefined,
|
|
192
|
-
requestId: requestId || undefined,
|
|
193
|
-
};
|
|
194
|
-
})
|
|
195
|
-
.filter((item) => item !== null)
|
|
196
|
-
.sort((a, b) => (b.createdAt ?? '').localeCompare(a.createdAt ?? ''))
|
|
197
|
-
.slice(0, limit);
|
|
198
|
-
return matches;
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
async readConversation(params) {
|
|
202
|
-
return this.withDatabase(async (db) => {
|
|
203
|
-
const composerId = (params.composerId ?? '').trim();
|
|
204
|
-
if (!composerId) {
|
|
205
|
-
throw new Error('缺少 composer_id');
|
|
206
|
-
}
|
|
207
|
-
const limit = Math.max(1, Math.min(params.limit ?? 200, 2000));
|
|
208
|
-
const includeEmpty = params.includeEmpty ?? false;
|
|
209
|
-
const rows = await allRows(db, 'select key, value from cursorDiskKV where key like ?', [`bubbleId:${composerId}:%`]);
|
|
210
|
-
const messages = rows
|
|
211
|
-
.map((row) => {
|
|
212
|
-
const parsed = parseJson(row.value);
|
|
213
|
-
if (!parsed) {
|
|
214
|
-
return null;
|
|
215
|
-
}
|
|
216
|
-
const text = asString(parsed.text);
|
|
217
|
-
if (!includeEmpty && !text) {
|
|
218
|
-
return null;
|
|
219
|
-
}
|
|
220
|
-
return {
|
|
221
|
-
bubbleId: extractBubbleId(row.key),
|
|
222
|
-
type: asNumber(parsed.type) ?? 0,
|
|
223
|
-
text,
|
|
224
|
-
createdAt: asString(parsed.createdAt) || undefined,
|
|
225
|
-
requestId: asString(parsed.requestId) || undefined,
|
|
226
|
-
};
|
|
227
|
-
})
|
|
228
|
-
.filter((item) => item !== null)
|
|
229
|
-
.sort((a, b) => (a.createdAt ?? '').localeCompare(b.createdAt ?? ''))
|
|
230
|
-
.slice(0, limit);
|
|
231
|
-
return {
|
|
232
|
-
composerId,
|
|
233
|
-
messages,
|
|
234
|
-
};
|
|
235
|
-
});
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
export function createCursorHistoryClient() {
|
|
239
|
-
return new CursorHistoryClient();
|
|
240
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
|
|
2
|
-
const readConversationMock = vi.fn();
|
|
3
|
-
vi.mock('../../lib/cursor-history-client.js', () => ({
|
|
4
|
-
createCursorHistoryClient: () => ({
|
|
5
|
-
readConversation: readConversationMock,
|
|
6
|
-
}),
|
|
7
|
-
}));
|
|
8
|
-
import { cursorReadConversation } from '../cursor_read_conversation.js';
|
|
9
|
-
beforeEach(() => {
|
|
10
|
-
readConversationMock.mockReset();
|
|
11
|
-
});
|
|
12
|
-
afterEach(() => {
|
|
13
|
-
vi.clearAllMocks();
|
|
14
|
-
});
|
|
15
|
-
describe('cursor history tools', () => {
|
|
16
|
-
test('cursor_read_conversation 返回消息时间线', async () => {
|
|
17
|
-
readConversationMock.mockResolvedValue({
|
|
18
|
-
composerId: 'c1',
|
|
19
|
-
messages: [
|
|
20
|
-
{ bubbleId: 'b1', type: 1, text: '我们先聊需求' },
|
|
21
|
-
{ bubbleId: 'b2', type: 2, text: '听懂了' },
|
|
22
|
-
],
|
|
23
|
-
});
|
|
24
|
-
const result = await cursorReadConversation({ composer_id: 'c1', limit: 50, include_empty: true });
|
|
25
|
-
expect(result.isError).toBe(false);
|
|
26
|
-
expect('structuredContent' in result).toBe(true);
|
|
27
|
-
if (!('structuredContent' in result)) {
|
|
28
|
-
throw new Error('structuredContent 缺失');
|
|
29
|
-
}
|
|
30
|
-
expect(result.content[0].text).toContain('共 2 条消息');
|
|
31
|
-
expect(result.structuredContent.messageCount).toBe(2);
|
|
32
|
-
expect(readConversationMock).toHaveBeenCalledWith({
|
|
33
|
-
composerId: 'c1',
|
|
34
|
-
limit: 50,
|
|
35
|
-
includeEmpty: true,
|
|
36
|
-
});
|
|
37
|
-
});
|
|
38
|
-
});
|