mcp-probe-kit 3.0.15 → 3.0.17

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 (104) hide show
  1. package/README.md +618 -410
  2. package/build/index.js +13 -1
  3. package/build/lib/__tests__/memory-client.unit.test.d.ts +1 -0
  4. package/build/lib/__tests__/memory-client.unit.test.js +83 -0
  5. package/build/lib/__tests__/memory-config.unit.test.d.ts +1 -0
  6. package/build/lib/__tests__/memory-config.unit.test.js +33 -0
  7. package/build/lib/cursor-history-client.d.ts +54 -0
  8. package/build/lib/cursor-history-client.js +240 -0
  9. package/build/lib/gitnexus-bridge.js +6 -8
  10. package/build/lib/memory-client.d.ts +61 -0
  11. package/build/lib/memory-client.js +293 -0
  12. package/build/lib/memory-config.d.ts +14 -0
  13. package/build/lib/memory-config.js +31 -0
  14. package/build/lib/memory-orchestration.d.ts +26 -0
  15. package/build/lib/memory-orchestration.js +65 -0
  16. package/build/lib/project-detector.js +6 -4
  17. package/build/lib/skill-bridge.d.ts +31 -0
  18. package/build/lib/skill-bridge.js +100 -0
  19. package/build/lib/workspace-root.d.ts +12 -0
  20. package/build/lib/workspace-root.js +153 -0
  21. package/build/resources/ui-ux-data/charts.json +302 -0
  22. package/build/resources/ui-ux-data/colors.json +1058 -0
  23. package/build/resources/ui-ux-data/icons.json +1102 -0
  24. package/build/resources/ui-ux-data/landing.json +262 -0
  25. package/build/resources/ui-ux-data/metadata.json +6 -0
  26. package/build/resources/ui-ux-data/products.json +1058 -0
  27. package/build/resources/ui-ux-data/react-performance.json +574 -0
  28. package/build/resources/ui-ux-data/stacks/astro.json +266 -0
  29. package/build/resources/ui-ux-data/stacks/flutter.json +626 -0
  30. package/build/resources/ui-ux-data/stacks/html-tailwind.json +662 -0
  31. package/build/resources/ui-ux-data/stacks/jetpack-compose.json +626 -0
  32. package/build/resources/ui-ux-data/stacks/nextjs.json +218 -0
  33. package/build/resources/ui-ux-data/stacks/nuxt-ui.json +14 -0
  34. package/build/resources/ui-ux-data/stacks/nuxtjs.json +182 -0
  35. package/build/resources/ui-ux-data/stacks/react-native.json +350 -0
  36. package/build/resources/ui-ux-data/stacks/react.json +530 -0
  37. package/build/resources/ui-ux-data/stacks/shadcn.json +566 -0
  38. package/build/resources/ui-ux-data/stacks/svelte.json +134 -0
  39. package/build/resources/ui-ux-data/stacks/swiftui.json +26 -0
  40. package/build/resources/ui-ux-data/stacks/vue.json +170 -0
  41. package/build/resources/ui-ux-data/styles.json +1610 -0
  42. package/build/resources/ui-ux-data/typography.json +743 -0
  43. package/build/resources/ui-ux-data/ui-reasoning.json +1431 -0
  44. package/build/resources/ui-ux-data/ux-guidelines.json +1190 -0
  45. package/build/resources/ui-ux-data/web-interface.json +389 -0
  46. package/build/schemas/code-analysis-tools.d.ts +1 -1
  47. package/build/schemas/code-analysis-tools.js +1 -1
  48. package/build/schemas/index.d.ts +198 -4
  49. package/build/schemas/index.js +2 -0
  50. package/build/schemas/memory-tools.d.ts +191 -0
  51. package/build/schemas/memory-tools.js +106 -0
  52. package/build/schemas/orchestration-tools.d.ts +3 -3
  53. package/build/schemas/orchestration-tools.js +3 -3
  54. package/build/schemas/ui-ux-schemas.d.ts +8 -0
  55. package/build/schemas/ui-ux-schemas.js +5 -1
  56. package/build/tools/__tests__/cursor-history.unit.test.d.ts +1 -0
  57. package/build/tools/__tests__/cursor-history.unit.test.js +87 -0
  58. package/build/tools/__tests__/memorize_asset.unit.test.d.ts +1 -0
  59. package/build/tools/__tests__/memorize_asset.unit.test.js +68 -0
  60. package/build/tools/code_insight.d.ts +20 -0
  61. package/build/tools/code_insight.js +15 -0
  62. package/build/tools/cursor_list_conversations.d.ts +7 -0
  63. package/build/tools/cursor_list_conversations.js +35 -0
  64. package/build/tools/cursor_read_conversation.d.ts +7 -0
  65. package/build/tools/cursor_read_conversation.js +36 -0
  66. package/build/tools/cursor_search_conversations.d.ts +7 -0
  67. package/build/tools/cursor_search_conversations.js +36 -0
  68. package/build/tools/index.d.ts +6 -0
  69. package/build/tools/index.js +7 -0
  70. package/build/tools/init_project_context.d.ts +20 -1
  71. package/build/tools/init_project_context.js +114 -99
  72. package/build/tools/memorize_asset.d.ts +7 -0
  73. package/build/tools/memorize_asset.js +66 -0
  74. package/build/tools/read_memory_asset.d.ts +7 -0
  75. package/build/tools/read_memory_asset.js +26 -0
  76. package/build/tools/scan_and_extract_patterns.d.ts +27 -0
  77. package/build/tools/scan_and_extract_patterns.js +346 -0
  78. package/build/tools/start_bugfix.d.ts +20 -0
  79. package/build/tools/start_bugfix.js +97 -69
  80. package/build/tools/start_feature.d.ts +20 -0
  81. package/build/tools/start_feature.js +61 -31
  82. package/build/tools/start_onboard.d.ts +20 -0
  83. package/build/tools/start_onboard.js +15 -0
  84. package/build/tools/start_product.js +8 -1
  85. package/build/tools/start_ui.d.ts +20 -0
  86. package/build/tools/start_ui.js +79 -34
  87. package/build/tools/ui-ux-tools.js +21 -17
  88. package/build/utils/ui-data-loader.d.ts +18 -2
  89. package/build/utils/ui-data-loader.js +74 -12
  90. package/docs/data/tools.js +472 -373
  91. package/docs/i18n/all-tools/en.json +38 -5
  92. package/docs/i18n/all-tools/ja.json +14 -4
  93. package/docs/i18n/all-tools/ko.json +13 -3
  94. package/docs/i18n/all-tools/zh-CN.json +38 -5
  95. package/docs/i18n/en.json +48 -10
  96. package/docs/i18n/ja.json +47 -9
  97. package/docs/i18n/ko.json +47 -9
  98. package/docs/i18n/zh-CN.json +48 -10
  99. package/docs/pages/all-tools.html +515 -515
  100. package/docs/pages/examples.html +661 -661
  101. package/docs/pages/getting-started.html +673 -582
  102. package/docs/pages/migration.html +291 -291
  103. package/package.json +83 -81
  104. package/docs/debug-i18n.html +0 -163
@@ -0,0 +1,293 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import { getMemoryConfig, isMemoryEnabled, isMemoryReadEnabled } from './memory-config.js';
3
+ function truncate(value, maxChars) {
4
+ if (value.length <= maxChars) {
5
+ return value;
6
+ }
7
+ return `${value.slice(0, Math.max(0, maxChars - 3))}...`;
8
+ }
9
+ function ensureArray(value) {
10
+ if (!Array.isArray(value)) {
11
+ return [];
12
+ }
13
+ return value.filter((item) => typeof item === 'string' && item.trim().length > 0);
14
+ }
15
+ function numberOr(value, fallback) {
16
+ return typeof value === 'number' && Number.isFinite(value) ? value : fallback;
17
+ }
18
+ function buildEmbeddingInput(input) {
19
+ return [
20
+ `name: ${input.name}`,
21
+ `type: ${input.type}`,
22
+ `description: ${input.description}`,
23
+ `summary: ${input.summary}`,
24
+ input.tags.length > 0 ? `tags: ${input.tags.join(', ')}` : '',
25
+ input.usage ? `usage: ${input.usage}` : '',
26
+ `content:\n${input.content}`,
27
+ ]
28
+ .filter(Boolean)
29
+ .join('\n\n');
30
+ }
31
+ export function normalizeContentForHash(content) {
32
+ return content
33
+ .replace(/\r\n?/g, '\n')
34
+ .split('\n')
35
+ .map((line) => line.replace(/[ \t]+$/g, ''))
36
+ .join('\n')
37
+ .replace(/\n{3,}/g, '\n\n')
38
+ .trim();
39
+ }
40
+ export function sha256Hex(value) {
41
+ return createHash('sha256').update(value).digest('hex');
42
+ }
43
+ export function buildMemoryContentHashes(content) {
44
+ return {
45
+ contentHash: sha256Hex(content),
46
+ normalizedContentHash: sha256Hex(normalizeContentForHash(content)),
47
+ };
48
+ }
49
+ export class MemoryClient {
50
+ config;
51
+ constructor(config = getMemoryConfig()) {
52
+ this.config = config;
53
+ }
54
+ isEnabled() {
55
+ return isMemoryEnabled(this.config);
56
+ }
57
+ isReadEnabled() {
58
+ return isMemoryReadEnabled(this.config);
59
+ }
60
+ buildHeaders(includeJson = true) {
61
+ const headers = {};
62
+ if (includeJson) {
63
+ headers['Content-Type'] = 'application/json';
64
+ }
65
+ if (this.config.qdrantApiKey) {
66
+ headers['api-key'] = this.config.qdrantApiKey;
67
+ }
68
+ return headers;
69
+ }
70
+ buildEmbeddingHeaders() {
71
+ const headers = {
72
+ 'Content-Type': 'application/json',
73
+ };
74
+ if (this.config.embeddingApiKey) {
75
+ headers.Authorization = `Bearer ${this.config.embeddingApiKey}`;
76
+ }
77
+ return headers;
78
+ }
79
+ async requestJson(url, init) {
80
+ const response = await fetch(url, init);
81
+ if (!response.ok) {
82
+ throw new Error(`HTTP ${response.status}: ${await response.text()}`);
83
+ }
84
+ return response.json();
85
+ }
86
+ async ensureCollection(vectorSize) {
87
+ const url = `${this.config.qdrantUrl}/collections/${encodeURIComponent(this.config.qdrantCollection)}`;
88
+ const exists = await fetch(url, { headers: this.buildHeaders(false) });
89
+ if (exists.ok) {
90
+ return;
91
+ }
92
+ if (exists.status !== 404) {
93
+ throw new Error(`Qdrant collection check failed: HTTP ${exists.status}`);
94
+ }
95
+ await this.requestJson(url, {
96
+ method: 'PUT',
97
+ headers: this.buildHeaders(),
98
+ body: JSON.stringify({
99
+ vectors: {
100
+ size: vectorSize,
101
+ distance: 'Cosine',
102
+ },
103
+ }),
104
+ });
105
+ }
106
+ async embed(text) {
107
+ if (!this.config.embeddingUrl) {
108
+ throw new Error('MEMORY_EMBEDDING_URL 未配置');
109
+ }
110
+ if (this.config.embeddingProvider === 'openai-compatible') {
111
+ const data = await this.requestJson(this.config.embeddingUrl, {
112
+ method: 'POST',
113
+ headers: this.buildEmbeddingHeaders(),
114
+ body: JSON.stringify({
115
+ model: this.config.embeddingModel,
116
+ input: text,
117
+ }),
118
+ });
119
+ const vector = data.data?.[0]?.embedding;
120
+ if (!vector || !Array.isArray(vector) || vector.length === 0) {
121
+ throw new Error('Embedding 服务未返回有效向量');
122
+ }
123
+ return vector;
124
+ }
125
+ const data = await this.requestJson(this.config.embeddingUrl, {
126
+ method: 'POST',
127
+ headers: this.buildEmbeddingHeaders(),
128
+ body: JSON.stringify({
129
+ model: this.config.embeddingModel,
130
+ prompt: text,
131
+ }),
132
+ });
133
+ if (!data.embedding || !Array.isArray(data.embedding) || data.embedding.length === 0) {
134
+ throw new Error('Embedding 服务未返回有效向量');
135
+ }
136
+ return data.embedding;
137
+ }
138
+ async findExistingAssetByNormalizedContentHash(normalizedContentHash) {
139
+ const data = await this.requestJson(`${this.config.qdrantUrl}/collections/${encodeURIComponent(this.config.qdrantCollection)}/points/scroll`, {
140
+ method: 'POST',
141
+ headers: this.buildHeaders(),
142
+ body: JSON.stringify({
143
+ limit: 1,
144
+ with_payload: true,
145
+ with_vectors: false,
146
+ filter: {
147
+ must: [
148
+ {
149
+ key: 'normalizedContentHash',
150
+ match: {
151
+ value: normalizedContentHash,
152
+ },
153
+ },
154
+ ],
155
+ },
156
+ }),
157
+ });
158
+ const payload = data.result?.points?.[0]?.payload;
159
+ if (!payload) {
160
+ return null;
161
+ }
162
+ return {
163
+ id: String(payload.id || ''),
164
+ name: String(payload.name || ''),
165
+ type: String(payload.type || ''),
166
+ description: String(payload.description || ''),
167
+ summary: String(payload.summary || ''),
168
+ content: String(payload.content || ''),
169
+ tags: ensureArray(payload.tags),
170
+ confidence: numberOr(payload.confidence, 0.5),
171
+ sourceProject: typeof payload.sourceProject === 'string' ? payload.sourceProject : undefined,
172
+ sourcePath: typeof payload.sourcePath === 'string' ? payload.sourcePath : undefined,
173
+ usage: typeof payload.usage === 'string' ? payload.usage : undefined,
174
+ contentHash: typeof payload.contentHash === 'string' ? payload.contentHash : undefined,
175
+ normalizedContentHash: typeof payload.normalizedContentHash === 'string' ? payload.normalizedContentHash : undefined,
176
+ createdAt: String(payload.createdAt || ''),
177
+ updatedAt: String(payload.updatedAt || ''),
178
+ };
179
+ }
180
+ async upsertAsset(input) {
181
+ if (!this.isEnabled()) {
182
+ throw new Error('记忆系统未启用');
183
+ }
184
+ const now = new Date().toISOString();
185
+ const hashes = buildMemoryContentHashes(input.content);
186
+ const asset = {
187
+ id: randomUUID(),
188
+ name: input.name,
189
+ type: input.type,
190
+ description: input.description,
191
+ summary: input.summary,
192
+ content: input.content,
193
+ tags: ensureArray(input.tags),
194
+ confidence: numberOr(input.confidence, 0.5),
195
+ sourceProject: input.sourceProject,
196
+ sourcePath: input.sourcePath,
197
+ usage: input.usage,
198
+ contentHash: hashes.contentHash,
199
+ normalizedContentHash: hashes.normalizedContentHash,
200
+ createdAt: now,
201
+ updatedAt: now,
202
+ };
203
+ const vector = await this.embed(buildEmbeddingInput({
204
+ name: asset.name,
205
+ type: asset.type,
206
+ description: asset.description,
207
+ summary: asset.summary,
208
+ tags: asset.tags,
209
+ usage: asset.usage,
210
+ content: asset.content,
211
+ }));
212
+ await this.ensureCollection(vector.length);
213
+ const existing = await this.findExistingAssetByNormalizedContentHash(asset.normalizedContentHash || '');
214
+ if (existing) {
215
+ return existing;
216
+ }
217
+ await this.requestJson(`${this.config.qdrantUrl}/collections/${encodeURIComponent(this.config.qdrantCollection)}/points?wait=true`, {
218
+ method: 'PUT',
219
+ headers: this.buildHeaders(),
220
+ body: JSON.stringify({
221
+ points: [
222
+ {
223
+ id: asset.id,
224
+ vector,
225
+ payload: asset,
226
+ },
227
+ ],
228
+ }),
229
+ });
230
+ return asset;
231
+ }
232
+ async search(query, limit = this.config.searchLimit) {
233
+ if (!this.isEnabled()) {
234
+ return [];
235
+ }
236
+ const vector = await this.embed(query);
237
+ const data = await this.requestJson(`${this.config.qdrantUrl}/collections/${encodeURIComponent(this.config.qdrantCollection)}/points/search`, {
238
+ method: 'POST',
239
+ headers: this.buildHeaders(),
240
+ body: JSON.stringify({
241
+ vector,
242
+ limit,
243
+ with_payload: true,
244
+ }),
245
+ });
246
+ return (data.result || []).map((point) => {
247
+ const payload = point.payload || {};
248
+ return {
249
+ id: String(point.id),
250
+ score: numberOr(point.score, 0),
251
+ name: String(payload.name || ''),
252
+ type: String(payload.type || ''),
253
+ description: String(payload.description || ''),
254
+ summary: truncate(String(payload.summary || ''), this.config.summaryMaxChars),
255
+ tags: ensureArray(payload.tags),
256
+ sourcePath: typeof payload.sourcePath === 'string' ? payload.sourcePath : undefined,
257
+ };
258
+ });
259
+ }
260
+ async getAsset(assetId) {
261
+ if (!this.isReadEnabled()) {
262
+ return null;
263
+ }
264
+ const data = await this.requestJson(`${this.config.qdrantUrl}/collections/${encodeURIComponent(this.config.qdrantCollection)}/points/${encodeURIComponent(assetId)}`, {
265
+ method: 'GET',
266
+ headers: this.buildHeaders(false),
267
+ });
268
+ const payload = data.result?.payload;
269
+ if (!payload) {
270
+ return null;
271
+ }
272
+ return {
273
+ id: String(payload.id || assetId),
274
+ name: String(payload.name || ''),
275
+ type: String(payload.type || ''),
276
+ description: String(payload.description || ''),
277
+ summary: String(payload.summary || ''),
278
+ content: String(payload.content || ''),
279
+ tags: ensureArray(payload.tags),
280
+ confidence: numberOr(payload.confidence, 0.5),
281
+ sourceProject: typeof payload.sourceProject === 'string' ? payload.sourceProject : undefined,
282
+ sourcePath: typeof payload.sourcePath === 'string' ? payload.sourcePath : undefined,
283
+ usage: typeof payload.usage === 'string' ? payload.usage : undefined,
284
+ contentHash: typeof payload.contentHash === 'string' ? payload.contentHash : undefined,
285
+ normalizedContentHash: typeof payload.normalizedContentHash === 'string' ? payload.normalizedContentHash : undefined,
286
+ createdAt: String(payload.createdAt || ''),
287
+ updatedAt: String(payload.updatedAt || ''),
288
+ };
289
+ }
290
+ }
291
+ export function createMemoryClient() {
292
+ return new MemoryClient();
293
+ }
@@ -0,0 +1,14 @@
1
+ export interface MemoryConfig {
2
+ qdrantUrl: string;
3
+ qdrantApiKey: string;
4
+ qdrantCollection: string;
5
+ embeddingUrl: string;
6
+ embeddingApiKey: string;
7
+ embeddingModel: string;
8
+ embeddingProvider: 'ollama' | 'openai-compatible';
9
+ searchLimit: number;
10
+ summaryMaxChars: number;
11
+ }
12
+ export declare function getMemoryConfig(): MemoryConfig;
13
+ export declare function isMemoryEnabled(config?: MemoryConfig): boolean;
14
+ export declare function isMemoryReadEnabled(config?: MemoryConfig): boolean;
@@ -0,0 +1,31 @@
1
+ function normalizeBaseUrl(value) {
2
+ return (value || '').trim().replace(/\/+$/, '');
3
+ }
4
+ function getNumberEnv(name, fallback) {
5
+ const raw = process.env[name]?.trim();
6
+ if (!raw) {
7
+ return fallback;
8
+ }
9
+ const parsed = Number(raw);
10
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
11
+ }
12
+ export function getMemoryConfig() {
13
+ const provider = (process.env.MEMORY_EMBEDDING_PROVIDER || 'ollama').trim().toLowerCase();
14
+ return {
15
+ qdrantUrl: normalizeBaseUrl(process.env.MEMORY_QDRANT_URL),
16
+ qdrantApiKey: (process.env.MEMORY_QDRANT_API_KEY || '').trim(),
17
+ qdrantCollection: (process.env.MEMORY_QDRANT_COLLECTION || 'mcp_probe_memory').trim(),
18
+ embeddingUrl: normalizeBaseUrl(process.env.MEMORY_EMBEDDING_URL),
19
+ embeddingApiKey: (process.env.MEMORY_EMBEDDING_API_KEY || '').trim(),
20
+ embeddingModel: (process.env.MEMORY_EMBEDDING_MODEL || 'nomic-embed-text').trim(),
21
+ embeddingProvider: provider === 'openai-compatible' ? 'openai-compatible' : 'ollama',
22
+ searchLimit: getNumberEnv('MEMORY_SEARCH_LIMIT', 3),
23
+ summaryMaxChars: getNumberEnv('MEMORY_SUMMARY_MAX_CHARS', 280),
24
+ };
25
+ }
26
+ export function isMemoryEnabled(config = getMemoryConfig()) {
27
+ return Boolean(config.qdrantUrl && config.embeddingUrl && config.embeddingModel);
28
+ }
29
+ export function isMemoryReadEnabled(config = getMemoryConfig()) {
30
+ return Boolean(config.qdrantUrl);
31
+ }
@@ -0,0 +1,26 @@
1
+ import type { MemorySearchResult } from './memory-client.js';
2
+ export interface MemoryInjectionContext {
3
+ enabled: boolean;
4
+ available: boolean;
5
+ degraded: boolean;
6
+ query: string;
7
+ results: MemorySearchResult[];
8
+ error?: string;
9
+ }
10
+ export declare function loadMemoryInjectionContext(query: string): Promise<MemoryInjectionContext>;
11
+ export declare function renderMemoryGuideSection(context: MemoryInjectionContext): string;
12
+ export declare function buildMemoryPlanStep(): {
13
+ id: string;
14
+ tool: string;
15
+ when: string;
16
+ args: {
17
+ name: string;
18
+ type: string;
19
+ description: string;
20
+ summary: string;
21
+ content: string;
22
+ usage: string;
23
+ confidence: number;
24
+ };
25
+ outputs: never[];
26
+ };
@@ -0,0 +1,65 @@
1
+ import { createMemoryClient } from './memory-client.js';
2
+ export async function loadMemoryInjectionContext(query) {
3
+ const client = createMemoryClient();
4
+ if (!client.isEnabled()) {
5
+ return {
6
+ enabled: false,
7
+ available: false,
8
+ degraded: false,
9
+ query,
10
+ results: [],
11
+ };
12
+ }
13
+ try {
14
+ const results = await client.search(query);
15
+ return {
16
+ enabled: true,
17
+ available: true,
18
+ degraded: false,
19
+ query,
20
+ results,
21
+ };
22
+ }
23
+ catch (error) {
24
+ return {
25
+ enabled: true,
26
+ available: false,
27
+ degraded: true,
28
+ query,
29
+ results: [],
30
+ error: error instanceof Error ? error.message : String(error),
31
+ };
32
+ }
33
+ }
34
+ export function renderMemoryGuideSection(context) {
35
+ if (!context.enabled) {
36
+ return '';
37
+ }
38
+ if (!context.available) {
39
+ return `\n\n## 🧠 记忆系统\n- 状态: 已配置但本次检索降级\n- 原因: ${context.error || '未知错误'}\n- 处理: 忽略记忆注入,继续主流程\n`;
40
+ }
41
+ if (context.results.length === 0) {
42
+ return `\n\n## 🧠 记忆系统\n- 状态: 已启用\n- 检索结果: 未找到高相关历史资产\n- 处理: 继续主流程;若本次产出存在高价值通用资产,结束后调用 \`memorize_asset\` 沉淀\n`;
43
+ }
44
+ const items = context.results
45
+ .map((item, index) => `${index + 1}. ${item.name} [${item.type}] score=${item.score.toFixed(3)}\n - 摘要: ${item.summary}\n - 读取: read_memory_asset {\"asset_id\": \"${item.id}\"}${item.sourcePath ? `\n - 来源: ${item.sourcePath}` : ''}`)
46
+ .join('\n');
47
+ return `\n\n## 🧠 记忆系统\n- 状态: 已启用\n- 指令: 优先复用以下历史成功经验;如果某条相关,再用 \`read_memory_asset\` 拉取完整内容,不要直接重写\n- 检索结果:\n${items}\n`;
48
+ }
49
+ export function buildMemoryPlanStep() {
50
+ return {
51
+ id: 'memorize',
52
+ tool: 'memorize_asset',
53
+ when: '本次实现完成且确认存在可复用资产',
54
+ args: {
55
+ name: '[资产名称]',
56
+ type: 'code',
57
+ description: '[该资产解决了什么问题]',
58
+ summary: '[用于后续检索的简洁摘要]',
59
+ content: '[可复用代码或规范内容]',
60
+ usage: '[适用场景与限制]',
61
+ confidence: 0.7,
62
+ },
63
+ outputs: [],
64
+ };
65
+ }
@@ -8,6 +8,7 @@
8
8
  */
9
9
  import * as fs from 'fs';
10
10
  import * as path from 'path';
11
+ import { resolveWorkspaceRoot } from './workspace-root.js';
11
12
  // 检测规则配置
12
13
  const detectionRules = {
13
14
  javascript: {
@@ -116,7 +117,8 @@ const detectionRules = {
116
117
  /**
117
118
  * 检测项目类型
118
119
  */
119
- export function detectProjectType(projectRoot = process.cwd()) {
120
+ export function detectProjectType(projectRoot) {
121
+ const resolvedProjectRoot = resolveWorkspaceRoot(projectRoot);
120
122
  const indicators = [];
121
123
  let detectedLanguage = 'unknown';
122
124
  let detectedFramework;
@@ -126,7 +128,7 @@ export function detectProjectType(projectRoot = process.cwd()) {
126
128
  for (const [language, rule] of Object.entries(detectionRules)) {
127
129
  // 检查语言特征文件是否存在
128
130
  const languageIndicator = rule.indicators.find(indicator => {
129
- const filePath = path.join(projectRoot, indicator);
131
+ const filePath = path.join(resolvedProjectRoot, indicator);
130
132
  return fs.existsSync(filePath);
131
133
  });
132
134
  if (languageIndicator) {
@@ -134,7 +136,7 @@ export function detectProjectType(projectRoot = process.cwd()) {
134
136
  indicators.push(languageIndicator);
135
137
  confidence = 50; // 基础置信度
136
138
  // 检测框架
137
- const frameworkResult = detectFramework(projectRoot, language, rule);
139
+ const frameworkResult = detectFramework(resolvedProjectRoot, language, rule);
138
140
  if (frameworkResult) {
139
141
  detectedFramework = frameworkResult.framework;
140
142
  detectedCategory = frameworkResult.category;
@@ -146,7 +148,7 @@ export function detectProjectType(projectRoot = process.cwd()) {
146
148
  }
147
149
  // 如果没有检测到语言,尝试通过文件扩展名判断
148
150
  if (detectedLanguage === 'unknown') {
149
- const languageByExtension = detectLanguageByExtension(projectRoot);
151
+ const languageByExtension = detectLanguageByExtension(resolvedProjectRoot);
150
152
  if (languageByExtension) {
151
153
  detectedLanguage = languageByExtension.language;
152
154
  confidence = languageByExtension.confidence;
@@ -0,0 +1,31 @@
1
+ export type SkillBridgeWorkflow = "start_ui" | "start_product";
2
+ export interface SkillBridgeItem {
3
+ name: string;
4
+ role: string;
5
+ installed: boolean;
6
+ skillPath?: string;
7
+ expectedPaths: string[];
8
+ }
9
+ export interface SkillBridgeStatus {
10
+ workflow: SkillBridgeWorkflow;
11
+ generatedAt: string;
12
+ installedCount: number;
13
+ missingCount: number;
14
+ ready: boolean;
15
+ skills: SkillBridgeItem[];
16
+ }
17
+ export declare function detectSkillBridge(workflow: SkillBridgeWorkflow): SkillBridgeStatus;
18
+ export declare function buildSkillHeaderNote(status: SkillBridgeStatus): string;
19
+ export declare function buildSkillBridgePlanStep(status: SkillBridgeStatus): {
20
+ id: string;
21
+ tool: string;
22
+ action: string;
23
+ when: string;
24
+ args: {
25
+ order: string[];
26
+ available: string[];
27
+ missing: string[];
28
+ };
29
+ outputs: never[];
30
+ };
31
+ export declare function renderSkillBridgeSection(status: SkillBridgeStatus): string;
@@ -0,0 +1,100 @@
1
+ import * as fs from "node:fs";
2
+ import * as os from "node:os";
3
+ import * as path from "node:path";
4
+ const SKILL_DEFINITIONS = [
5
+ {
6
+ name: "ui-ux-pro-max",
7
+ role: "设计系统与风格策略推荐",
8
+ appliesTo: ["start_ui", "start_product"],
9
+ },
10
+ {
11
+ name: "interaction-design",
12
+ role: "交互状态与流程约束",
13
+ appliesTo: ["start_ui", "start_product"],
14
+ },
15
+ {
16
+ name: "frontend-design",
17
+ role: "前端视觉与代码实现增强",
18
+ appliesTo: ["start_ui", "start_product"],
19
+ },
20
+ ];
21
+ function buildSkillRoots() {
22
+ const fromEnv = (process.env.MCP_SKILLS_ROOTS || "")
23
+ .split(path.delimiter)
24
+ .map((item) => item.trim())
25
+ .filter(Boolean);
26
+ const home = os.homedir();
27
+ const defaults = [
28
+ path.join(home, ".agents", "skills"),
29
+ path.join(home, ".codex", "skills"),
30
+ path.join(home, ".codex", "skills", ".system"),
31
+ ];
32
+ return Array.from(new Set([...fromEnv, ...defaults]));
33
+ }
34
+ function resolveSkill(name, roots) {
35
+ const expectedPaths = roots.map((root) => path.join(root, name, "SKILL.md"));
36
+ const installedPath = expectedPaths.find((candidate) => fs.existsSync(candidate));
37
+ return {
38
+ name,
39
+ role: SKILL_DEFINITIONS.find((item) => item.name === name)?.role || "",
40
+ installed: Boolean(installedPath),
41
+ skillPath: installedPath,
42
+ expectedPaths,
43
+ };
44
+ }
45
+ export function detectSkillBridge(workflow) {
46
+ const roots = buildSkillRoots();
47
+ const selected = SKILL_DEFINITIONS.filter((item) => item.appliesTo.includes(workflow));
48
+ const skills = selected.map((item) => resolveSkill(item.name, roots));
49
+ const installedCount = skills.filter((item) => item.installed).length;
50
+ const missingCount = skills.length - installedCount;
51
+ return {
52
+ workflow,
53
+ generatedAt: new Date().toISOString(),
54
+ installedCount,
55
+ missingCount,
56
+ ready: missingCount === 0,
57
+ skills,
58
+ };
59
+ }
60
+ export function buildSkillHeaderNote(status) {
61
+ return `Skill Bridge: ${status.installedCount}/${status.skills.length} 可用`;
62
+ }
63
+ export function buildSkillBridgePlanStep(status) {
64
+ return {
65
+ id: "skill-bridge",
66
+ tool: "manual",
67
+ action: "invoke_skills",
68
+ when: "在宿主支持 skill 调用时优先执行",
69
+ args: {
70
+ order: status.skills.map((item) => item.name),
71
+ available: status.skills.filter((item) => item.installed).map((item) => item.name),
72
+ missing: status.skills.filter((item) => !item.installed).map((item) => item.name),
73
+ },
74
+ outputs: [],
75
+ };
76
+ }
77
+ export function renderSkillBridgeSection(status) {
78
+ const orderLines = status.skills
79
+ .map((item, index) => `${index + 1}. \`${item.name}\` - ${item.role}`)
80
+ .join("\n");
81
+ const stateLines = status.skills
82
+ .map((item) => `- ${item.name}: ${item.installed ? `已安装(${item.skillPath})` : "未安装"}`)
83
+ .join("\n");
84
+ const conclusion = status.ready
85
+ ? "全部 skill 可用,建议按顺序调用后再执行 MCP 工具步骤。"
86
+ : "部分 skill 缺失:继续执行 MCP 主流程,不阻塞;安装缺失 skill 后下次运行可获得更高质量输出。";
87
+ return `## 🧩 Skill Bridge(UI/PRD 增强)
88
+
89
+ 按顺序调用(宿主支持时):
90
+ ${orderLines}
91
+
92
+ 当前状态:
93
+ ${stateLines}
94
+
95
+ ${conclusion}
96
+
97
+ ---
98
+
99
+ `;
100
+ }
@@ -0,0 +1,12 @@
1
+ export declare function isLikelyProjectNamedRelativePath(inputPath?: string): boolean;
2
+ export declare function buildProjectRootRetryHint(inputPath?: string): {
3
+ preferred: {
4
+ project_root: string;
5
+ path: string;
6
+ };
7
+ fallback: {
8
+ project_root: string;
9
+ };
10
+ };
11
+ export declare function resolveWorkspaceRoot(explicitProjectRoot?: string): string;
12
+ export declare function toWorkspacePath(explicitProjectRoot?: string): string;