mta-mcp 1.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +457 -0
- package/README.md +680 -0
- package/archive/docs/CLAUDE_SETUP.md +103 -0
- package/archive/docs/DEVELOPMENT_ROADMAP.md +186 -0
- package/archive/docs/GETTING_STARTED.md +337 -0
- package/archive/docs/MIGRATION_GUIDE.md +189 -0
- package/archive/docs/TROUBLESHOOTING.md +386 -0
- package/archive/docs/USAGE_GUIDE.md +739 -0
- package/archive/tests/test-cloud-dev.cjs +158 -0
- package/archive/tests/test-mcp.sh +46 -0
- package/archive/tests/test-miniprogram.cjs +94 -0
- package/archive/tests/test-phase2.cjs +144 -0
- package/archive/tests/test-phase3.cjs +121 -0
- package/archive/tests/test-phase4.cjs +249 -0
- package/archive/tests/test-standards.cjs +111 -0
- package/archive/tests/test-vitasage-config.cjs +25 -0
- package/build/core/agentTemplate.d.ts +33 -0
- package/build/core/agentTemplate.d.ts.map +1 -0
- package/build/core/agentTemplate.js +158 -0
- package/build/core/agentTemplate.js.map +1 -0
- package/build/core/codeValidator.d.ts +82 -0
- package/build/core/codeValidator.d.ts.map +1 -0
- package/build/core/codeValidator.js +287 -0
- package/build/core/codeValidator.js.map +1 -0
- package/build/core/githubClient.d.ts +26 -0
- package/build/core/githubClient.d.ts.map +1 -0
- package/build/core/githubClient.js +60 -0
- package/build/core/githubClient.js.map +1 -0
- package/build/core/i18nDetector.d.ts +47 -0
- package/build/core/i18nDetector.d.ts.map +1 -0
- package/build/core/i18nDetector.js +314 -0
- package/build/core/i18nDetector.js.map +1 -0
- package/build/core/projectContextManager.d.ts +39 -0
- package/build/core/projectContextManager.d.ts.map +1 -0
- package/build/core/projectContextManager.js +147 -0
- package/build/core/projectContextManager.js.map +1 -0
- package/build/core/smartAgentMatcher.d.ts +51 -0
- package/build/core/smartAgentMatcher.d.ts.map +1 -0
- package/build/core/smartAgentMatcher.js +493 -0
- package/build/core/smartAgentMatcher.js.map +1 -0
- package/build/core/standardsManager.d.ts +130 -0
- package/build/core/standardsManager.d.ts.map +1 -0
- package/build/core/standardsManager.js +595 -0
- package/build/core/standardsManager.js.map +1 -0
- package/build/core/types.d.ts +55 -0
- package/build/core/types.d.ts.map +1 -0
- package/build/core/types.js +21 -0
- package/build/core/types.js.map +1 -0
- package/build/index.d.ts +3 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +463 -0
- package/build/index.js.map +1 -0
- package/build/tools/analyzeProject.d.ts +12 -0
- package/build/tools/analyzeProject.d.ts.map +1 -0
- package/build/tools/analyzeProject.js +85 -0
- package/build/tools/analyzeProject.js.map +1 -0
- package/build/tools/autoSetup.d.ts +15 -0
- package/build/tools/autoSetup.d.ts.map +1 -0
- package/build/tools/autoSetup.js +291 -0
- package/build/tools/autoSetup.js.map +1 -0
- package/build/tools/generateConfig.d.ts +16 -0
- package/build/tools/generateConfig.d.ts.map +1 -0
- package/build/tools/generateConfig.js +379 -0
- package/build/tools/generateConfig.js.map +1 -0
- package/build/tools/generateProjectAgent.d.ts +15 -0
- package/build/tools/generateProjectAgent.d.ts.map +1 -0
- package/build/tools/generateProjectAgent.js +348 -0
- package/build/tools/generateProjectAgent.js.map +1 -0
- package/build/tools/getCompactStandards.d.ts +20 -0
- package/build/tools/getCompactStandards.d.ts.map +1 -0
- package/build/tools/getCompactStandards.js +367 -0
- package/build/tools/getCompactStandards.js.map +1 -0
- package/build/tools/getSmartStandards.d.ts +14 -0
- package/build/tools/getSmartStandards.d.ts.map +1 -0
- package/build/tools/getSmartStandards.js +184 -0
- package/build/tools/getSmartStandards.js.map +1 -0
- package/build/tools/healthCheck.d.ts +14 -0
- package/build/tools/healthCheck.d.ts.map +1 -0
- package/build/tools/healthCheck.js +237 -0
- package/build/tools/healthCheck.js.map +1 -0
- package/build/tools/listAgents.d.ts +10 -0
- package/build/tools/listAgents.d.ts.map +1 -0
- package/build/tools/listAgents.js +79 -0
- package/build/tools/listAgents.js.map +1 -0
- package/build/tools/matchAgents.d.ts +14 -0
- package/build/tools/matchAgents.d.ts.map +1 -0
- package/build/tools/matchAgents.js +70 -0
- package/build/tools/matchAgents.js.map +1 -0
- package/build/tools/usePreset.d.ts +23 -0
- package/build/tools/usePreset.d.ts.map +1 -0
- package/build/tools/usePreset.js +163 -0
- package/build/tools/usePreset.js.map +1 -0
- package/generate-project-config.cjs +97 -0
- package/package.json +45 -0
- package/regenerate-vitasage.sh +30 -0
- package/src/core/codeValidator.ts +357 -0
- package/src/core/githubClient.ts +64 -0
- package/src/core/i18nDetector.ts +357 -0
- package/src/core/smartAgentMatcher.ts +490 -0
- package/src/core/standardsManager.ts +763 -0
- package/src/core/types.ts +72 -0
- package/src/index.ts +519 -0
- package/src/tools/analyzeProject.ts +94 -0
- package/src/tools/autoSetup.ts +312 -0
- package/src/tools/generateConfig.ts +429 -0
- package/src/tools/getCompactStandards.ts +413 -0
- package/src/tools/getSmartStandards.ts +205 -0
- package/src/tools/healthCheck.ts +261 -0
- package/src/tools/listAgents.ts +91 -0
- package/src/tools/matchAgents.ts +80 -0
- package/src/tools/usePreset.ts +180 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import { StandardsManager } from '../core/standardsManager.js';
|
|
3
|
+
import { ConsoleLogger } from '../core/types.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 紧凑规范工具 - Token 优化版
|
|
7
|
+
*
|
|
8
|
+
* 优化策略:
|
|
9
|
+
* 1. 只返回规范摘要而非完整内容
|
|
10
|
+
* 2. 提供规范 URI 供按需加载
|
|
11
|
+
* 3. 针对特定场景返回精简的关键规则
|
|
12
|
+
*/
|
|
13
|
+
export async function getCompactStandards(args: {
|
|
14
|
+
currentFile?: string;
|
|
15
|
+
fileContent?: string;
|
|
16
|
+
scenario?: string;
|
|
17
|
+
// 新增:返回模式
|
|
18
|
+
mode?: 'summary' | 'key-rules' | 'full';
|
|
19
|
+
}): Promise<{
|
|
20
|
+
content: Array<{ type: string; text: string }>;
|
|
21
|
+
}> {
|
|
22
|
+
const logger = new ConsoleLogger();
|
|
23
|
+
const manager = new StandardsManager();
|
|
24
|
+
const mode = args.mode || 'key-rules';
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
// 检测上下文(复用智能检测逻辑)
|
|
28
|
+
const context = detectContext(args, logger);
|
|
29
|
+
|
|
30
|
+
// 获取相关规范 URI
|
|
31
|
+
const standardUris = manager.getRelevantStandards({
|
|
32
|
+
fileType: context.fileType,
|
|
33
|
+
imports: context.imports,
|
|
34
|
+
scenario: args.scenario || context.scenario
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// 根据模式返回不同内容
|
|
38
|
+
let responseContent: any;
|
|
39
|
+
|
|
40
|
+
switch (mode) {
|
|
41
|
+
case 'summary':
|
|
42
|
+
// 只返回规范名称和描述,约 500 tokens
|
|
43
|
+
responseContent = buildSummaryResponse(standardUris, manager);
|
|
44
|
+
break;
|
|
45
|
+
|
|
46
|
+
case 'key-rules':
|
|
47
|
+
// 返回关键规则摘要,约 2000-3000 tokens
|
|
48
|
+
responseContent = buildKeyRulesResponse(standardUris, context, manager);
|
|
49
|
+
break;
|
|
50
|
+
|
|
51
|
+
case 'full':
|
|
52
|
+
// 完整内容(原有行为)
|
|
53
|
+
responseContent = buildFullResponse(standardUris, manager);
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
content: [{
|
|
59
|
+
type: 'text',
|
|
60
|
+
text: JSON.stringify(responseContent, null, 2)
|
|
61
|
+
}]
|
|
62
|
+
};
|
|
63
|
+
} catch (error) {
|
|
64
|
+
logger.error(`规范获取失败: ${error}`);
|
|
65
|
+
return {
|
|
66
|
+
content: [{
|
|
67
|
+
type: 'text',
|
|
68
|
+
text: JSON.stringify({
|
|
69
|
+
error: error instanceof Error ? error.message : String(error)
|
|
70
|
+
}, null, 2)
|
|
71
|
+
}]
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* 检测上下文
|
|
78
|
+
*/
|
|
79
|
+
function detectContext(args: any, logger: ConsoleLogger): {
|
|
80
|
+
fileType: string;
|
|
81
|
+
imports: string[];
|
|
82
|
+
scenario: string;
|
|
83
|
+
} {
|
|
84
|
+
let fileType = 'unknown';
|
|
85
|
+
let imports: string[] = [];
|
|
86
|
+
let scenario = '';
|
|
87
|
+
|
|
88
|
+
// 从文件路径检测
|
|
89
|
+
if (args.currentFile && fs.existsSync(args.currentFile)) {
|
|
90
|
+
const ext = args.currentFile.split('.').pop()?.toLowerCase() || '';
|
|
91
|
+
const extMap: Record<string, string> = {
|
|
92
|
+
'vue': 'vue', 'ts': 'ts', 'tsx': 'tsx',
|
|
93
|
+
'js': 'js', 'jsx': 'jsx', 'dart': 'dart'
|
|
94
|
+
};
|
|
95
|
+
fileType = extMap[ext] || 'unknown';
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
const content = fs.readFileSync(args.currentFile, 'utf-8');
|
|
99
|
+
imports = extractImports(content);
|
|
100
|
+
scenario = inferScenario(content, fileType);
|
|
101
|
+
} catch {
|
|
102
|
+
// 忽略读取错误
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// 从内容检测
|
|
107
|
+
if (args.fileContent) {
|
|
108
|
+
imports = [...imports, ...extractImports(args.fileContent)];
|
|
109
|
+
if (fileType === 'unknown') {
|
|
110
|
+
if (args.fileContent.includes('<template>')) fileType = 'vue';
|
|
111
|
+
else if (args.fileContent.includes('interface ')) fileType = 'ts';
|
|
112
|
+
}
|
|
113
|
+
scenario = scenario || inferScenario(args.fileContent, fileType);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return { fileType, imports: [...new Set(imports)], scenario };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* 构建摘要响应(约 500 tokens)
|
|
121
|
+
*/
|
|
122
|
+
function buildSummaryResponse(uris: string[], manager: StandardsManager): any {
|
|
123
|
+
const standards = manager.getAvailableStandards();
|
|
124
|
+
const matched = uris.map(uri => {
|
|
125
|
+
const std = standards.find(s => s.uri === uri);
|
|
126
|
+
return {
|
|
127
|
+
uri,
|
|
128
|
+
name: std?.name || uri,
|
|
129
|
+
description: std?.description || ''
|
|
130
|
+
};
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
success: true,
|
|
135
|
+
mode: 'summary',
|
|
136
|
+
message: '已识别相关规范,如需详细内容请使用 mode: "key-rules" 或 "full"',
|
|
137
|
+
standards: matched,
|
|
138
|
+
stats: {
|
|
139
|
+
count: matched.length,
|
|
140
|
+
estimatedTokens: 500
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* 构建关键规则响应(约 2000-3000 tokens)
|
|
147
|
+
* 针对不同场景提取关键规则
|
|
148
|
+
*/
|
|
149
|
+
function buildKeyRulesResponse(
|
|
150
|
+
uris: string[],
|
|
151
|
+
context: { fileType: string; imports: string[]; scenario: string },
|
|
152
|
+
manager: StandardsManager
|
|
153
|
+
): any {
|
|
154
|
+
const keyRules: string[] = [];
|
|
155
|
+
|
|
156
|
+
// 根据文件类型添加关键规则
|
|
157
|
+
if (context.fileType === 'vue') {
|
|
158
|
+
keyRules.push(...getVueKeyRules(context.imports));
|
|
159
|
+
} else if (context.fileType === 'dart') {
|
|
160
|
+
keyRules.push(...getFlutterKeyRules());
|
|
161
|
+
} else if (context.fileType === 'ts' || context.fileType === 'tsx') {
|
|
162
|
+
keyRules.push(...getTypeScriptKeyRules());
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// 根据导入添加特定规则
|
|
166
|
+
if (context.imports.includes('element-plus')) {
|
|
167
|
+
keyRules.push(...getElementPlusKeyRules());
|
|
168
|
+
}
|
|
169
|
+
if (context.imports.some(i => i.includes('i18n') || i.includes('locale'))) {
|
|
170
|
+
keyRules.push(...getI18nKeyRules());
|
|
171
|
+
}
|
|
172
|
+
if (context.imports.includes('pinia')) {
|
|
173
|
+
keyRules.push(...getPiniaKeyRules());
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
success: true,
|
|
178
|
+
mode: 'key-rules',
|
|
179
|
+
context: {
|
|
180
|
+
fileType: context.fileType,
|
|
181
|
+
detectedImports: context.imports,
|
|
182
|
+
scenario: context.scenario
|
|
183
|
+
},
|
|
184
|
+
keyRules: keyRules.join('\n\n'),
|
|
185
|
+
standardUris: uris, // 提供完整 URI 供按需加载
|
|
186
|
+
stats: {
|
|
187
|
+
rulesCount: keyRules.length,
|
|
188
|
+
estimatedTokens: Math.ceil(keyRules.join('').length / 4)
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* 构建完整响应(原有行为)
|
|
195
|
+
*/
|
|
196
|
+
function buildFullResponse(uris: string[], manager: StandardsManager): any {
|
|
197
|
+
const content = manager.combineStandards(uris);
|
|
198
|
+
return {
|
|
199
|
+
success: true,
|
|
200
|
+
mode: 'full',
|
|
201
|
+
standards: uris,
|
|
202
|
+
content,
|
|
203
|
+
stats: {
|
|
204
|
+
standardsCount: uris.length,
|
|
205
|
+
contentLength: content.length,
|
|
206
|
+
estimatedTokens: Math.ceil(content.length / 4)
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// ==================== 关键规则提取 ====================
|
|
212
|
+
|
|
213
|
+
function getVueKeyRules(imports: string[]): string[] {
|
|
214
|
+
return [
|
|
215
|
+
`## Vue 3 关键规则
|
|
216
|
+
|
|
217
|
+
1. **使用 \`<script setup lang="ts">\`** - 必须使用 Composition API
|
|
218
|
+
2. **Props/Emits 类型定义** - 必须使用 interface 定义类型
|
|
219
|
+
3. **禁止内联样式** - 使用 scoped CSS 或 class
|
|
220
|
+
4. **模板简洁** - 复杂逻辑提取到 computed 或方法
|
|
221
|
+
5. **响应式** - 基本类型用 ref,对象用 reactive(不重新赋值)`,
|
|
222
|
+
|
|
223
|
+
`## 组件结构顺序
|
|
224
|
+
\`\`\`vue
|
|
225
|
+
<script setup lang="ts">
|
|
226
|
+
// 1. 类型导入
|
|
227
|
+
// 2. Props/Emits 定义
|
|
228
|
+
// 3. 响应式状态
|
|
229
|
+
// 4. 计算属性
|
|
230
|
+
// 5. 方法
|
|
231
|
+
// 6. 生命周期
|
|
232
|
+
</script>
|
|
233
|
+
\`\`\``
|
|
234
|
+
];
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function getElementPlusKeyRules(): string[] {
|
|
238
|
+
return [
|
|
239
|
+
`## Element Plus 关键规则
|
|
240
|
+
|
|
241
|
+
### 表格规范
|
|
242
|
+
- **必须** \`border\` + \`highlight-current-row\`
|
|
243
|
+
- 操作列 \`fixed="right"\`,宽度:2按钮160px/3按钮240px/4按钮280px
|
|
244
|
+
- 使用 \`v-loading\` 显示加载状态
|
|
245
|
+
|
|
246
|
+
### 表单规范
|
|
247
|
+
- 使用 \`label-position="top"\` 或 \`label-width="120px"\`
|
|
248
|
+
- 验证规则使用 \`FormRules<T>\` 类型
|
|
249
|
+
- 验证消息必须国际化
|
|
250
|
+
|
|
251
|
+
### 弹窗规范
|
|
252
|
+
- 使用 \`destroy-on-close\` 重置状态
|
|
253
|
+
- 宽度:简单500px/标准700px/复杂900px
|
|
254
|
+
|
|
255
|
+
### 消息提示
|
|
256
|
+
\`\`\`typescript
|
|
257
|
+
ElMessage.success($t('保存成功'))
|
|
258
|
+
ElMessageBox.confirm($t('确认删除?'), $t('警告'), { type: 'warning' })
|
|
259
|
+
\`\`\``,
|
|
260
|
+
|
|
261
|
+
`## 表格示例
|
|
262
|
+
\`\`\`vue
|
|
263
|
+
<el-table v-loading="listLoading" :data="list" border highlight-current-row>
|
|
264
|
+
<el-table-column type="index" :label="$t('序号')" width="70" />
|
|
265
|
+
<el-table-column prop="name" :label="$t('名称')" min-width="120" />
|
|
266
|
+
<el-table-column fixed="right" :label="$t('操作')" width="240">
|
|
267
|
+
<template #default="{ row }">
|
|
268
|
+
<el-button link type="primary" @click="handleEdit(row)">{{ $t('编辑') }}</el-button>
|
|
269
|
+
<el-button link type="danger" @click="handleDelete(row)">{{ $t('删除') }}</el-button>
|
|
270
|
+
</template>
|
|
271
|
+
</el-table-column>
|
|
272
|
+
</el-table>
|
|
273
|
+
\`\`\``
|
|
274
|
+
];
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function getI18nKeyRules(): string[] {
|
|
278
|
+
return [
|
|
279
|
+
`## 国际化关键规则
|
|
280
|
+
|
|
281
|
+
### 必须国际化的内容
|
|
282
|
+
- 所有按钮文字:\`{{ $t('保存') }}\`
|
|
283
|
+
- 表单标签:\`:label="$t('客户名称')"\`
|
|
284
|
+
- 占位符:\`:placeholder="$t('请输入')"\`
|
|
285
|
+
- 表格列标题:\`:label="$t('订单编号')"\`
|
|
286
|
+
- 弹窗标题:\`:title="$t('新增客户')"\`
|
|
287
|
+
- 提示消息:\`ElMessage.success($t('保存成功'))\`
|
|
288
|
+
- 验证规则:\`message: $t('请输入用户名')\`
|
|
289
|
+
|
|
290
|
+
### 禁止
|
|
291
|
+
- ❌ \`<el-button>新增</el-button>\` (硬编码)
|
|
292
|
+
- ❌ \`label="客户名称"\` (硬编码)
|
|
293
|
+
- ❌ \`ElMessage.success('保存成功')\` (硬编码)`
|
|
294
|
+
];
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function getPiniaKeyRules(): string[] {
|
|
298
|
+
return [
|
|
299
|
+
`## Pinia 关键规则
|
|
300
|
+
|
|
301
|
+
### Setup Store 语法(推荐)
|
|
302
|
+
\`\`\`typescript
|
|
303
|
+
export const useUserStore = defineStore('user', () => {
|
|
304
|
+
const user = ref<User | null>(null)
|
|
305
|
+
const isLoggedIn = computed(() => !!user.value)
|
|
306
|
+
|
|
307
|
+
async function login(credentials: Credentials) {
|
|
308
|
+
user.value = await authApi.login(credentials)
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
return { user, isLoggedIn, login }
|
|
312
|
+
})
|
|
313
|
+
\`\`\`
|
|
314
|
+
|
|
315
|
+
### 规范要求
|
|
316
|
+
- Store ID 使用小写短横线命名
|
|
317
|
+
- 使用 Setup Store 语法而非 Options Store
|
|
318
|
+
- 异步操作直接用 async/await
|
|
319
|
+
- 复杂状态操作使用 $patch`
|
|
320
|
+
];
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function getTypeScriptKeyRules(): string[] {
|
|
324
|
+
return [
|
|
325
|
+
`## TypeScript 关键规则
|
|
326
|
+
|
|
327
|
+
### 类型定义
|
|
328
|
+
- 必须定义函数参数和返回类型
|
|
329
|
+
- 使用 interface 定义对象类型
|
|
330
|
+
- 避免使用 any,使用 unknown 替代
|
|
331
|
+
|
|
332
|
+
### 命名规范
|
|
333
|
+
- 变量/函数:camelCase
|
|
334
|
+
- 类/接口/类型:PascalCase
|
|
335
|
+
- 常量:UPPER_SNAKE_CASE
|
|
336
|
+
|
|
337
|
+
### 示例
|
|
338
|
+
\`\`\`typescript
|
|
339
|
+
interface User {
|
|
340
|
+
id: number
|
|
341
|
+
name: string
|
|
342
|
+
email?: string
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
async function fetchUser(id: number): Promise<User | null> {
|
|
346
|
+
// ...
|
|
347
|
+
}
|
|
348
|
+
\`\`\``
|
|
349
|
+
];
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function getFlutterKeyRules(): string[] {
|
|
353
|
+
return [
|
|
354
|
+
`## Flutter UI 系统关键规则
|
|
355
|
+
|
|
356
|
+
### Design Token 快捷方式(必须使用)
|
|
357
|
+
| 快捷方式 | 用途 | 示例 |
|
|
358
|
+
|---------|------|------|
|
|
359
|
+
| \`$c\` | 颜色 | \`$c.primary\`, \`$c.text.primary\` |
|
|
360
|
+
| \`$s\` | 间距 | \`$s.sm\`, \`$s.md\`, \`$s.px(14)\` |
|
|
361
|
+
| \`$t\` | 排版 | \`$t.displayLarge\`, \`$t.bodyMedium\` |
|
|
362
|
+
| \`$r\` | 圆角 | \`$r.sm\`, \`$r.md\`, \`$r.full\` |
|
|
363
|
+
|
|
364
|
+
### Flex 组件库(必须使用)
|
|
365
|
+
- \`FlexButton\` 替代 ElevatedButton/TextButton
|
|
366
|
+
- \`FlexCard\` 替代 Card/Container
|
|
367
|
+
- \`FlexInput\` 替代 TextField
|
|
368
|
+
- \`Gap\` 替代 SizedBox(间隔)
|
|
369
|
+
|
|
370
|
+
### 禁止
|
|
371
|
+
- ❌ \`Color(0xFF3B82F6)\` 硬编码颜色
|
|
372
|
+
- ❌ \`EdgeInsets.all(16)\` 硬编码间距
|
|
373
|
+
- ❌ \`Colors.blue\` Material 颜色常量`
|
|
374
|
+
];
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// ==================== 辅助函数 ====================
|
|
378
|
+
|
|
379
|
+
function extractImports(content: string): string[] {
|
|
380
|
+
const imports: string[] = [];
|
|
381
|
+
|
|
382
|
+
// ES6 imports
|
|
383
|
+
const esImportRegex = /import\s+(?:(?:\{[^}]*\}|\*\s+as\s+\w+|\w+)\s+from\s+)?['"]([^'"]+)['"]/g;
|
|
384
|
+
let match;
|
|
385
|
+
while ((match = esImportRegex.exec(content)) !== null) {
|
|
386
|
+
imports.push(match[1]);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// require
|
|
390
|
+
const requireRegex = /require\(['"]([^'"]+)['"]\)/g;
|
|
391
|
+
while ((match = requireRegex.exec(content)) !== null) {
|
|
392
|
+
imports.push(match[1]);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
return imports;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function inferScenario(content: string, fileType: string): string {
|
|
399
|
+
const scenarios: string[] = [];
|
|
400
|
+
const lower = content.toLowerCase();
|
|
401
|
+
|
|
402
|
+
if (lower.includes('el-table') || lower.includes('el-form')) {
|
|
403
|
+
scenarios.push('表格组件', '表单组件');
|
|
404
|
+
}
|
|
405
|
+
if (lower.includes('$t(') || lower.includes('i18n')) {
|
|
406
|
+
scenarios.push('国际化');
|
|
407
|
+
}
|
|
408
|
+
if (lower.includes('interface ') || lower.includes('type ')) {
|
|
409
|
+
scenarios.push('类型定义');
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
return scenarios.join('、');
|
|
413
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import { StandardsManager } from '../core/standardsManager.js';
|
|
3
|
+
import { ConsoleLogger } from '../core/types.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 智能规范推荐工具
|
|
7
|
+
* 零参数,自动检测上下文并推荐规范
|
|
8
|
+
*/
|
|
9
|
+
export async function getSmartStandards(args: {
|
|
10
|
+
currentFile?: string;
|
|
11
|
+
fileContent?: string;
|
|
12
|
+
}): Promise<{
|
|
13
|
+
content: Array<{ type: string; text: string }>;
|
|
14
|
+
}> {
|
|
15
|
+
const logger = new ConsoleLogger();
|
|
16
|
+
const manager = new StandardsManager();
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
let detectedFileType = 'unknown';
|
|
20
|
+
let detectedImports: string[] = [];
|
|
21
|
+
let detectedScenario = '';
|
|
22
|
+
let analysisSource = 'none';
|
|
23
|
+
|
|
24
|
+
// 策略 1: 使用提供的文件路径
|
|
25
|
+
if (args.currentFile && fs.existsSync(args.currentFile)) {
|
|
26
|
+
analysisSource = 'file-path';
|
|
27
|
+
const ext = args.currentFile.split('.').pop()?.toLowerCase() || '';
|
|
28
|
+
|
|
29
|
+
const extMap: Record<string, string> = {
|
|
30
|
+
'vue': 'vue',
|
|
31
|
+
'ts': 'ts',
|
|
32
|
+
'tsx': 'tsx',
|
|
33
|
+
'js': 'js',
|
|
34
|
+
'jsx': 'jsx'
|
|
35
|
+
};
|
|
36
|
+
detectedFileType = extMap[ext] || 'unknown';
|
|
37
|
+
|
|
38
|
+
// 读取文件内容分析
|
|
39
|
+
try {
|
|
40
|
+
const content = fs.readFileSync(args.currentFile, 'utf-8');
|
|
41
|
+
const imports = extractImports(content);
|
|
42
|
+
detectedImports = imports;
|
|
43
|
+
detectedScenario = inferScenario(content, detectedFileType);
|
|
44
|
+
} catch {
|
|
45
|
+
logger.log('无法读取文件内容,仅使用文件类型');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// 策略 2: 使用提供的文件内容
|
|
50
|
+
if (args.fileContent) {
|
|
51
|
+
analysisSource = 'file-content';
|
|
52
|
+
const imports = extractImports(args.fileContent);
|
|
53
|
+
detectedImports = [...detectedImports, ...imports];
|
|
54
|
+
|
|
55
|
+
// 从内容推断文件类型
|
|
56
|
+
if (detectedFileType === 'unknown') {
|
|
57
|
+
if (args.fileContent.includes('<template>')) {
|
|
58
|
+
detectedFileType = 'vue';
|
|
59
|
+
} else if (args.fileContent.includes('interface ') || args.fileContent.includes('type ')) {
|
|
60
|
+
detectedFileType = 'ts';
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const scenario = inferScenario(args.fileContent, detectedFileType);
|
|
65
|
+
if (scenario) detectedScenario = scenario;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// 策略 3: 环境检测(进程当前目录)
|
|
69
|
+
if (detectedFileType === 'unknown') {
|
|
70
|
+
analysisSource = 'environment';
|
|
71
|
+
const cwd = process.cwd();
|
|
72
|
+
const packageJsonPath = `${cwd}/package.json`;
|
|
73
|
+
|
|
74
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
75
|
+
try {
|
|
76
|
+
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
77
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
78
|
+
|
|
79
|
+
if (deps['vue']) {
|
|
80
|
+
detectedFileType = 'vue';
|
|
81
|
+
detectedImports.push('vue');
|
|
82
|
+
}
|
|
83
|
+
if (deps['react']) {
|
|
84
|
+
detectedFileType = 'tsx';
|
|
85
|
+
detectedImports.push('react');
|
|
86
|
+
}
|
|
87
|
+
if (deps['element-plus']) detectedImports.push('element-plus');
|
|
88
|
+
if (deps['pinia']) detectedImports.push('pinia');
|
|
89
|
+
if (deps['vue-i18n']) detectedImports.push('vue-i18n');
|
|
90
|
+
} catch {
|
|
91
|
+
logger.log('无法解析 package.json');
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// 去重
|
|
97
|
+
detectedImports = [...new Set(detectedImports)];
|
|
98
|
+
|
|
99
|
+
logger.log(`🔍 智能检测结果: fileType=${detectedFileType}, imports=${detectedImports.join(',')}, scenario=${detectedScenario}`);
|
|
100
|
+
|
|
101
|
+
// 获取相关规范
|
|
102
|
+
const standards = manager.getRelevantStandards({
|
|
103
|
+
fileType: detectedFileType !== 'unknown' ? detectedFileType : undefined,
|
|
104
|
+
imports: detectedImports.length > 0 ? detectedImports : undefined,
|
|
105
|
+
scenario: detectedScenario || undefined
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const combinedContent = manager.combineStandards(standards);
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
content: [{
|
|
112
|
+
type: 'text',
|
|
113
|
+
text: JSON.stringify({
|
|
114
|
+
success: true,
|
|
115
|
+
analysis: {
|
|
116
|
+
source: analysisSource,
|
|
117
|
+
fileType: detectedFileType,
|
|
118
|
+
imports: detectedImports,
|
|
119
|
+
scenario: detectedScenario
|
|
120
|
+
},
|
|
121
|
+
standards: standards,
|
|
122
|
+
content: combinedContent,
|
|
123
|
+
stats: {
|
|
124
|
+
standardsCount: standards.length,
|
|
125
|
+
contentLength: combinedContent.length,
|
|
126
|
+
estimatedTokens: Math.ceil(combinedContent.length / 4)
|
|
127
|
+
}
|
|
128
|
+
}, null, 2)
|
|
129
|
+
}]
|
|
130
|
+
};
|
|
131
|
+
} catch (error) {
|
|
132
|
+
logger.error(`智能规范推荐失败: ${error}`);
|
|
133
|
+
return {
|
|
134
|
+
content: [{
|
|
135
|
+
type: 'text',
|
|
136
|
+
text: JSON.stringify({
|
|
137
|
+
error: error instanceof Error ? error.message : String(error)
|
|
138
|
+
}, null, 2)
|
|
139
|
+
}]
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* 从代码中提取导入语句
|
|
146
|
+
*/
|
|
147
|
+
function extractImports(content: string): string[] {
|
|
148
|
+
const imports: string[] = [];
|
|
149
|
+
|
|
150
|
+
// ES6 imports
|
|
151
|
+
const es6Regex = /import\s+.*?\s+from\s+['"]([^'"]+)['"]/g;
|
|
152
|
+
let match;
|
|
153
|
+
while ((match = es6Regex.exec(content)) !== null) {
|
|
154
|
+
const pkg = match[1];
|
|
155
|
+
if (!pkg.startsWith('.') && !pkg.startsWith('/')) {
|
|
156
|
+
imports.push(pkg.split('/')[0]);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// require statements
|
|
161
|
+
const requireRegex = /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
162
|
+
while ((match = requireRegex.exec(content)) !== null) {
|
|
163
|
+
const pkg = match[1];
|
|
164
|
+
if (!pkg.startsWith('.') && !pkg.startsWith('/')) {
|
|
165
|
+
imports.push(pkg.split('/')[0]);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return [...new Set(imports)];
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* 从代码推断开发场景
|
|
174
|
+
*/
|
|
175
|
+
function inferScenario(content: string, fileType: string): string {
|
|
176
|
+
const scenarios: string[] = [];
|
|
177
|
+
|
|
178
|
+
// Vue 相关
|
|
179
|
+
if (fileType === 'vue') {
|
|
180
|
+
if (content.includes('ElForm') || content.includes('<el-form')) {
|
|
181
|
+
scenarios.push('表单组件');
|
|
182
|
+
}
|
|
183
|
+
if (content.includes('ElTable') || content.includes('<el-table')) {
|
|
184
|
+
scenarios.push('表格组件');
|
|
185
|
+
}
|
|
186
|
+
if (content.includes('defineStore')) {
|
|
187
|
+
scenarios.push('状态管理');
|
|
188
|
+
}
|
|
189
|
+
if (content.includes('useI18n') || content.includes('$t(')) {
|
|
190
|
+
scenarios.push('国际化');
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// API 调用
|
|
195
|
+
if (content.includes('fetch(') || content.includes('axios.')) {
|
|
196
|
+
scenarios.push('API 调用');
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// TypeScript
|
|
200
|
+
if (content.includes('interface ') || content.includes('type ')) {
|
|
201
|
+
scenarios.push('类型定义');
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return scenarios.join('、');
|
|
205
|
+
}
|