autosnippet 3.0.2 → 3.0.6

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 (50) hide show
  1. package/README.md +85 -239
  2. package/bin/mcp-server.js +12 -1
  3. package/dashboard/dist/assets/{icons-Cdq22n2i.js → icons-eQ_rWCus.js} +97 -102
  4. package/dashboard/dist/assets/index-B3Nnkdxi.js +133 -0
  5. package/dashboard/dist/assets/index-BFNDAqh3.css +1 -0
  6. package/dashboard/dist/index.html +3 -3
  7. package/lib/cli/SetupService.js +9 -12
  8. package/lib/cli/UpgradeService.js +15 -17
  9. package/lib/core/AstAnalyzer.js +2 -2
  10. package/lib/core/ast/ensure-grammars.js +2 -0
  11. package/lib/core/ast/index.js +8 -0
  12. package/lib/core/ast/lang-rust.js +695 -0
  13. package/lib/core/discovery/PythonDiscoverer.js +3 -0
  14. package/lib/core/discovery/RustDiscoverer.js +467 -0
  15. package/lib/core/discovery/index.js +3 -0
  16. package/lib/core/enhancement/django-enhancement.js +169 -3
  17. package/lib/core/enhancement/fastapi-enhancement.js +149 -3
  18. package/lib/core/enhancement/go-grpc-enhancement.js +4 -0
  19. package/lib/core/enhancement/go-web-enhancement.js +6 -0
  20. package/lib/core/enhancement/index.js +5 -0
  21. package/lib/core/enhancement/langchain-enhancement.js +233 -0
  22. package/lib/core/enhancement/ml-enhancement.js +265 -0
  23. package/lib/core/enhancement/nextjs-enhancement.js +219 -0
  24. package/lib/core/enhancement/node-server-enhancement.js +178 -4
  25. package/lib/core/enhancement/react-enhancement.js +165 -4
  26. package/lib/core/enhancement/rust-tokio-enhancement.js +231 -0
  27. package/lib/core/enhancement/rust-web-enhancement.js +256 -0
  28. package/lib/core/enhancement/spring-enhancement.js +2 -0
  29. package/lib/core/enhancement/vue-enhancement.js +143 -2
  30. package/lib/external/ai/AiProvider.js +45 -6
  31. package/lib/external/mcp/handlers/bootstrap/skills.js +2 -0
  32. package/lib/external/mcp/handlers/bootstrap.js +33 -9
  33. package/lib/external/mcp/handlers/guard.js +42 -0
  34. package/lib/http/routes/candidates.js +7 -1
  35. package/lib/service/chat/ChatAgent.js +1 -0
  36. package/lib/service/chat/tools.js +5 -1
  37. package/lib/service/guard/ComplianceReporter.js +20 -7
  38. package/lib/service/guard/GuardCheckEngine.js +156 -5
  39. package/lib/service/guard/SourceFileCollector.js +15 -0
  40. package/package.json +28 -6
  41. package/scripts/install-vscode-copilot.js +32 -97
  42. package/scripts/setup-mcp-config.js +18 -36
  43. package/skills/autosnippet-coldstart/SKILL.md +4 -2
  44. package/skills/autosnippet-concepts/SKILL.md +5 -3
  45. package/skills/autosnippet-reference-rust/SKILL.md +401 -0
  46. package/skills/autosnippet-structure/SKILL.md +1 -1
  47. package/templates/recipes-setup/README.md +2 -2
  48. package/templates/recipes-setup/_template.md +1 -1
  49. package/dashboard/dist/assets/index-ClkyPkDX.js +0 -133
  50. package/dashboard/dist/assets/index-t4QrJwv1.css +0 -1
@@ -0,0 +1,231 @@
1
+ /**
2
+ * Rust Tokio Async Enhancement Pack
3
+ * 条件: { languages: ['rust'], frameworks: ['tokio', 'async-std'] }
4
+ *
5
+ * 覆盖 Rust 异步运行时生态:
6
+ * - Tokio runtime (最主流)
7
+ * - async-std
8
+ * - 异步任务 spawn / JoinHandle
9
+ * - Channel (mpsc/oneshot/broadcast/watch)
10
+ * - 同步原语 (Mutex/RwLock/Semaphore)
11
+ * - 超时与取消 (select!, timeout)
12
+ */
13
+
14
+ import { EnhancementPack } from './EnhancementPack.js';
15
+
16
+ class RustTokioEnhancement extends EnhancementPack {
17
+ get id() {
18
+ return 'rust-tokio';
19
+ }
20
+ get displayName() {
21
+ return 'Rust Tokio/Async Enhancement';
22
+ }
23
+ get conditions() {
24
+ return {
25
+ languages: ['rust'],
26
+ frameworks: ['tokio', 'async-std'],
27
+ };
28
+ }
29
+
30
+ getExtraDimensions() {
31
+ return [
32
+ {
33
+ id: 'rust-async-task-scan',
34
+ label: 'Async Task 分析',
35
+ guide:
36
+ 'Rust 异步任务拓扑分析 — tokio::spawn / task::spawn_blocking 使用分布、JoinHandle 收集与 abort 策略、tokio::select! 分支、graceful shutdown 模式',
37
+ knowledgeTypes: ['architecture', 'code-pattern'],
38
+ skillWorthy: true,
39
+ dualOutput: true,
40
+ skillMeta: {
41
+ name: 'project-rust-async-tasks',
42
+ description:
43
+ 'Rust async task spawn/join topology, select branches and shutdown patterns (auto-generated by enhancement)',
44
+ },
45
+ },
46
+ {
47
+ id: 'rust-channel-sync-scan',
48
+ label: 'Channel/同步原语分析',
49
+ guide:
50
+ 'Rust Channel 与同步原语分析 — mpsc/oneshot/broadcast/watch channel 使用、Arc<Mutex>/Arc<RwLock> 共享状态、Semaphore 限流、Notify 事件',
51
+ knowledgeTypes: ['architecture', 'code-pattern'],
52
+ skillWorthy: true,
53
+ dualOutput: true,
54
+ skillMeta: {
55
+ name: 'project-rust-concurrency',
56
+ description:
57
+ 'Rust channel patterns, shared state synchronization and concurrency primitives (auto-generated by enhancement)',
58
+ },
59
+ },
60
+ ];
61
+ }
62
+
63
+ getGuardRules() {
64
+ return [
65
+ {
66
+ ruleId: 'rust-tokio-std-mutex',
67
+ category: 'correctness',
68
+ dimension: 'file',
69
+ severity: 'warning',
70
+ languages: ['rust'],
71
+ pattern: /std::sync::Mutex|std::sync::RwLock/,
72
+ message:
73
+ '在 async 代码中使用 std::sync::Mutex 会阻塞运行时线程,应使用 tokio::sync::Mutex 或 parking_lot::Mutex(如果临界区极短)',
74
+ },
75
+ {
76
+ ruleId: 'rust-tokio-blocking-in-async',
77
+ category: 'performance',
78
+ dimension: 'file',
79
+ severity: 'warning',
80
+ languages: ['rust'],
81
+ pattern: /std::thread::sleep|std::fs::|std::net::TcpStream/,
82
+ message:
83
+ 'async 上下文中不应使用阻塞操作,使用 tokio::time::sleep / tokio::fs / tokio::net 或 spawn_blocking()',
84
+ },
85
+ {
86
+ ruleId: 'rust-tokio-unbounded-channel',
87
+ category: 'performance',
88
+ dimension: 'file',
89
+ severity: 'info',
90
+ languages: ['rust'],
91
+ pattern: /unbounded_channel|mpsc::unbounded/,
92
+ message:
93
+ 'unbounded channel 在高吞吐场景下可能导致内存无限增长,考虑使用 bounded channel 并处理背压',
94
+ },
95
+ {
96
+ ruleId: 'rust-tokio-spawn-no-handle',
97
+ category: 'correctness',
98
+ dimension: 'file',
99
+ severity: 'info',
100
+ languages: ['rust'],
101
+ pattern: /tokio::spawn\([^)]+\)\s*;/,
102
+ message:
103
+ 'tokio::spawn 返回的 JoinHandle 被丢弃 — 任务 panic 不会传播,考虑使用 JoinSet 或 .await handle',
104
+ },
105
+ ];
106
+ }
107
+
108
+ detectPatterns(astSummary) {
109
+ const patterns = [];
110
+
111
+ // ── Async functions (potential task entry points) ──
112
+ let asyncFnCount = 0;
113
+ for (const m of astSummary.methods || []) {
114
+ if (m.isAsync) {
115
+ asyncFnCount++;
116
+ }
117
+ }
118
+ if (asyncFnCount > 0) {
119
+ patterns.push({
120
+ type: 'rust-async-functions',
121
+ count: asyncFnCount,
122
+ confidence: 0.85,
123
+ });
124
+ }
125
+
126
+ // ── Runtime main entry (#[tokio::main]) ──
127
+ for (const m of astSummary.methods || []) {
128
+ if (m.name === 'main' && m.isAsync) {
129
+ patterns.push({
130
+ type: 'rust-tokio-main',
131
+ line: m.line,
132
+ confidence: 0.95,
133
+ });
134
+ }
135
+ }
136
+
137
+ // ── Structs wrapping async primitives ──
138
+ for (const cls of astSummary.classes || []) {
139
+ if (cls.kind !== 'struct') continue;
140
+ const nameLower = cls.name.toLowerCase();
141
+ if (
142
+ nameLower.includes('worker') ||
143
+ nameLower.includes('executor') ||
144
+ nameLower.includes('scheduler') ||
145
+ nameLower.includes('dispatcher') ||
146
+ nameLower.includes('runtime') ||
147
+ nameLower.includes('pool')
148
+ ) {
149
+ patterns.push({
150
+ type: 'rust-async-worker',
151
+ className: cls.name,
152
+ line: cls.line,
153
+ confidence: 0.7,
154
+ });
155
+ }
156
+ }
157
+
158
+ // ── Channel / sync wrappers ──
159
+ for (const cls of astSummary.classes || []) {
160
+ if (cls.kind !== 'struct') continue;
161
+ const nameLower = cls.name.toLowerCase();
162
+ if (
163
+ nameLower.includes('sender') ||
164
+ nameLower.includes('receiver') ||
165
+ nameLower.includes('channel') ||
166
+ nameLower.includes('notifier') ||
167
+ nameLower.includes('broker') ||
168
+ nameLower.includes('bus')
169
+ ) {
170
+ patterns.push({
171
+ type: 'rust-channel-struct',
172
+ className: cls.name,
173
+ line: cls.line,
174
+ confidence: 0.7,
175
+ });
176
+ }
177
+ }
178
+
179
+ // ── Trait impls for async patterns ──
180
+ for (const cls of astSummary.classes || []) {
181
+ // Look for trait: Future / Stream / Service / Tower Layer
182
+ if (cls.kind === 'trait_impl') {
183
+ const traitName = cls.traitName || '';
184
+ if (
185
+ traitName === 'Future' ||
186
+ traitName === 'Stream' ||
187
+ traitName === 'Service' ||
188
+ traitName === 'Layer'
189
+ ) {
190
+ patterns.push({
191
+ type: 'rust-async-trait-impl',
192
+ className: cls.name,
193
+ traitName,
194
+ line: cls.line,
195
+ confidence: 0.85,
196
+ });
197
+ }
198
+ }
199
+ }
200
+
201
+ // ── Tokio/async imports ──
202
+ const asyncImports = (astSummary.imports || []).filter(
203
+ (imp) =>
204
+ imp.includes('tokio') ||
205
+ imp.includes('async_std') ||
206
+ imp.includes('futures') ||
207
+ imp.includes('tower')
208
+ );
209
+ if (asyncImports.length > 0) {
210
+ patterns.push({
211
+ type: 'rust-async-ecosystem-usage',
212
+ importCount: asyncImports.length,
213
+ confidence: 0.9,
214
+ });
215
+ }
216
+
217
+ // ── Detect common async patterns from lang-rust detectPatterns ──
218
+ for (const p of astSummary.patterns || []) {
219
+ if (p.type === 'async-heavy' || p.type === 'unsafe-usage') {
220
+ patterns.push({
221
+ type: `rust-tokio-${p.type}`,
222
+ confidence: p.confidence || 0.7,
223
+ });
224
+ }
225
+ }
226
+
227
+ return patterns;
228
+ }
229
+ }
230
+
231
+ export const pack = new RustTokioEnhancement();
@@ -0,0 +1,256 @@
1
+ /**
2
+ * Rust Web Enhancement Pack
3
+ * 条件: { languages: ['rust'], frameworks: ['actix-web', 'axum', 'rocket', 'warp'] }
4
+ *
5
+ * 覆盖主流 Rust HTTP 框架:
6
+ * - Axum (tower-based, 最流行)
7
+ * - Actix-web (actor 模型)
8
+ * - Rocket (宏驱动)
9
+ * - Warp (filter 组合)
10
+ */
11
+
12
+ import { EnhancementPack } from './EnhancementPack.js';
13
+
14
+ class RustWebEnhancement extends EnhancementPack {
15
+ get id() {
16
+ return 'rust-web';
17
+ }
18
+ get displayName() {
19
+ return 'Rust Web (Axum/Actix-web/Rocket/Warp) Enhancement';
20
+ }
21
+ get conditions() {
22
+ return {
23
+ languages: ['rust'],
24
+ frameworks: ['actix-web', 'axum', 'rocket', 'warp'],
25
+ };
26
+ }
27
+
28
+ getExtraDimensions() {
29
+ return [
30
+ {
31
+ id: 'rust-handler-scan',
32
+ label: 'HTTP Handler 分析',
33
+ guide:
34
+ 'Rust Web Handler 与 Middleware/Layer 分析 — handler 函数签名 (Extractor 模式)、State/AppData 共享、Tower Layer/Service 注册顺序、错误响应统一化 (IntoResponse)',
35
+ knowledgeTypes: ['architecture', 'code-pattern'],
36
+ skillWorthy: true,
37
+ dualOutput: true,
38
+ skillMeta: {
39
+ name: 'project-rust-handlers',
40
+ description:
41
+ 'Rust web handler patterns, extractors, state management and middleware layers (auto-generated by enhancement)',
42
+ },
43
+ },
44
+ {
45
+ id: 'rust-route-scan',
46
+ label: 'API Route 结构',
47
+ guide:
48
+ 'Rust 路由结构分析 — Router::new().route() / web::resource() 路由表、路由嵌套与合并 (nest/merge)、HTTP 方法分布、Path/Query/Json Extractor 使用',
49
+ knowledgeTypes: ['architecture'],
50
+ skillWorthy: true,
51
+ dualOutput: true,
52
+ skillMeta: {
53
+ name: 'project-rust-routes',
54
+ description:
55
+ 'Rust API route structure, nesting/merging and extractor usage (auto-generated by enhancement)',
56
+ },
57
+ },
58
+ ];
59
+ }
60
+
61
+ getGuardRules() {
62
+ return [
63
+ {
64
+ ruleId: 'rust-web-unwrap-in-handler',
65
+ category: 'correctness',
66
+ dimension: 'file',
67
+ severity: 'warning',
68
+ languages: ['rust'],
69
+ pattern: /\.unwrap\(\)/,
70
+ message:
71
+ 'Web handler 中避免 .unwrap(),应使用 ? 运算符配合自定义错误类型实现 IntoResponse,防止 500 panic',
72
+ },
73
+ {
74
+ ruleId: 'rust-web-blocking-in-async',
75
+ category: 'performance',
76
+ dimension: 'file',
77
+ severity: 'warning',
78
+ languages: ['rust'],
79
+ pattern: /std::fs::|std::net::|std::thread::sleep/,
80
+ message:
81
+ 'async handler 中不应使用 std 阻塞 I/O,应使用 tokio::fs / tokio::net / tokio::time::sleep 替代',
82
+ },
83
+ {
84
+ ruleId: 'rust-web-clone-state',
85
+ category: 'performance',
86
+ dimension: 'file',
87
+ severity: 'info',
88
+ languages: ['rust'],
89
+ pattern: /\.clone\(\)\s*[;,]\s*(?:\/\/.*)?$/m,
90
+ message:
91
+ '频繁 .clone() 共享状态可能有性能问题,考虑使用 Arc<T> 或 Axum State extractor / Actix Data<T>',
92
+ },
93
+ ];
94
+ }
95
+
96
+ detectPatterns(astSummary) {
97
+ const patterns = [];
98
+
99
+ // ── Handler functions (async fn with web extractor params) ──
100
+ for (const m of astSummary.methods || []) {
101
+ if (!m.className && m.isAsync) {
102
+ const nameLower = m.name.toLowerCase();
103
+ if (
104
+ nameLower.startsWith('get_') ||
105
+ nameLower.startsWith('post_') ||
106
+ nameLower.startsWith('put_') ||
107
+ nameLower.startsWith('delete_') ||
108
+ nameLower.startsWith('patch_') ||
109
+ nameLower.startsWith('handle_') ||
110
+ nameLower.startsWith('list_') ||
111
+ nameLower.startsWith('create_') ||
112
+ nameLower.startsWith('update_') ||
113
+ nameLower.startsWith('remove_') ||
114
+ nameLower === 'index' ||
115
+ nameLower === 'health' ||
116
+ nameLower === 'health_check'
117
+ ) {
118
+ patterns.push({
119
+ type: 'rust-web-handler',
120
+ methodName: m.name,
121
+ line: m.line,
122
+ confidence: 0.75,
123
+ });
124
+ }
125
+ }
126
+ }
127
+
128
+ // ── Structs used as State / AppData ──
129
+ for (const cls of astSummary.classes || []) {
130
+ if (cls.kind !== 'struct') continue;
131
+ const nameLower = cls.name.toLowerCase();
132
+ if (
133
+ nameLower.includes('state') ||
134
+ nameLower.includes('appdata') ||
135
+ nameLower.includes('appstate') ||
136
+ nameLower.includes('context') ||
137
+ nameLower.includes('config')
138
+ ) {
139
+ patterns.push({
140
+ type: 'rust-web-state',
141
+ className: cls.name,
142
+ line: cls.line,
143
+ confidence: 0.7,
144
+ });
145
+ }
146
+ }
147
+
148
+ // ── Error response types (impl IntoResponse / ResponseError) ──
149
+ for (const cls of astSummary.classes || []) {
150
+ const nameLower = cls.name.toLowerCase();
151
+ if (
152
+ nameLower.includes('error') ||
153
+ nameLower.includes('apierror') ||
154
+ nameLower.includes('appresponse')
155
+ ) {
156
+ patterns.push({
157
+ type: 'rust-web-error-type',
158
+ className: cls.name,
159
+ line: cls.line,
160
+ confidence: 0.7,
161
+ });
162
+ }
163
+ }
164
+
165
+ // ── Middleware / Layer structs ──
166
+ for (const cls of astSummary.classes || []) {
167
+ if (cls.kind !== 'struct') continue;
168
+ const nameLower = cls.name.toLowerCase();
169
+ if (
170
+ nameLower.includes('middleware') ||
171
+ nameLower.includes('layer') ||
172
+ nameLower.includes('auth') ||
173
+ nameLower.includes('cors') ||
174
+ nameLower.includes('ratelimit') ||
175
+ nameLower.includes('logging')
176
+ ) {
177
+ patterns.push({
178
+ type: 'rust-web-middleware',
179
+ className: cls.name,
180
+ line: cls.line,
181
+ confidence: 0.7,
182
+ });
183
+ }
184
+ }
185
+
186
+ // ── Extractor structs (used as handler params) ──
187
+ for (const cls of astSummary.classes || []) {
188
+ if (cls.kind !== 'struct') continue;
189
+ // Derive-heavy DTOs used as Json<T>, Query<T>, Path<T>
190
+ if (cls.derives && cls.derives.length >= 2) {
191
+ const hasSerdeDerive = cls.derives.some(
192
+ (d) => d === 'Deserialize' || d === 'Serialize'
193
+ );
194
+ if (hasSerdeDerive) {
195
+ const nameLower = cls.name.toLowerCase();
196
+ if (
197
+ nameLower.includes('request') ||
198
+ nameLower.includes('payload') ||
199
+ nameLower.includes('params') ||
200
+ nameLower.includes('query') ||
201
+ nameLower.includes('body') ||
202
+ nameLower.includes('form') ||
203
+ nameLower.includes('dto')
204
+ ) {
205
+ patterns.push({
206
+ type: 'rust-web-extractor-dto',
207
+ className: cls.name,
208
+ line: cls.line,
209
+ confidence: 0.85,
210
+ });
211
+ }
212
+ }
213
+ }
214
+ }
215
+
216
+ // ── Response structs ──
217
+ for (const cls of astSummary.classes || []) {
218
+ if (cls.kind !== 'struct') continue;
219
+ const nameLower = cls.name.toLowerCase();
220
+ if (
221
+ nameLower.includes('response') ||
222
+ nameLower.endsWith('resp') ||
223
+ nameLower.endsWith('reply')
224
+ ) {
225
+ patterns.push({
226
+ type: 'rust-web-response-dto',
227
+ className: cls.name,
228
+ line: cls.line,
229
+ confidence: 0.75,
230
+ });
231
+ }
232
+ }
233
+
234
+ // ── Web framework imports ──
235
+ const webImports = (astSummary.imports || []).filter(
236
+ (imp) =>
237
+ imp.includes('actix_web') ||
238
+ imp.includes('axum') ||
239
+ imp.includes('rocket') ||
240
+ imp.includes('warp') ||
241
+ imp.includes('tower') ||
242
+ imp.includes('tower_http')
243
+ );
244
+ if (webImports.length > 0) {
245
+ patterns.push({
246
+ type: 'rust-web-framework-usage',
247
+ importCount: webImports.length,
248
+ confidence: 0.9,
249
+ });
250
+ }
251
+
252
+ return patterns;
253
+ }
254
+ }
255
+
256
+ export const pack = new RustWebEnhancement();
@@ -52,6 +52,8 @@ class SpringEnhancement extends EnhancementPack {
52
52
  return [
53
53
  {
54
54
  ruleId: 'spring-field-injection',
55
+ category: 'style',
56
+ dimension: 'file',
55
57
  severity: 'warning',
56
58
  languages: ['java', 'kotlin'],
57
59
  pattern: /@Autowired\s+(?:private|protected)\s/,
@@ -1,6 +1,14 @@
1
1
  /**
2
2
  * Vue Enhancement Pack
3
3
  * 条件: { languages: ['typescript', 'javascript'], frameworks: ['vue', 'nuxt'] }
4
+ *
5
+ * 覆盖 Vue 3.5+ Composition API 生态:
6
+ * - Composable 函数 (useXxx)
7
+ * - Pinia Store 模式
8
+ * - SFC <script setup> 预处理
9
+ * - defineProps / defineEmits / defineModel 宏
10
+ * - VueRouter 导航守卫
11
+ * - provide/inject 依赖注入
4
12
  */
5
13
 
6
14
  import { EnhancementPack } from './EnhancementPack.js';
@@ -10,7 +18,7 @@ class VueEnhancement extends EnhancementPack {
10
18
  return 'vue';
11
19
  }
12
20
  get displayName() {
13
- return 'Vue / Nuxt Enhancement';
21
+ return 'Vue 3.5 / Nuxt Enhancement';
14
22
  }
15
23
  get conditions() {
16
24
  return { languages: ['typescript', 'javascript'], frameworks: ['vue', 'nuxt'] };
@@ -21,7 +29,7 @@ class VueEnhancement extends EnhancementPack {
21
29
  {
22
30
  id: 'composable-scan',
23
31
  label: 'Composable 函数分析',
24
- guide: 'Composable 函数(useXxx)+ 内部 ref/computed/watch 调用、provide/inject 模式',
32
+ guide: 'Composable 函数(useXxx)+ 内部 ref/computed/watch 调用、Composable 组合链、provide/inject 依赖注入模式',
25
33
  knowledgeTypes: ['code-pattern'],
26
34
  skillWorthy: true,
27
35
  dualOutput: true,
@@ -31,6 +39,34 @@ class VueEnhancement extends EnhancementPack {
31
39
  'Vue Composable patterns and composition API usage (auto-generated by enhancement)',
32
40
  },
33
41
  },
42
+ {
43
+ id: 'pinia-store-scan',
44
+ label: 'Pinia Store 分析',
45
+ guide:
46
+ 'Pinia Store 拓扑分析 — defineStore 定义、state/getters/actions 结构、Store 间依赖、$subscribe/$onAction 订阅模式、setup vs option 语法',
47
+ knowledgeTypes: ['architecture', 'code-pattern'],
48
+ skillWorthy: true,
49
+ dualOutput: true,
50
+ skillMeta: {
51
+ name: 'project-vue-stores',
52
+ description:
53
+ 'Pinia store definitions, dependencies and subscription patterns (auto-generated by enhancement)',
54
+ },
55
+ },
56
+ {
57
+ id: 'vue-component-api-scan',
58
+ label: '组件 API 分析',
59
+ guide:
60
+ 'Vue 组件 API 分析 — defineProps/defineEmits/defineModel/defineExpose 使用分布、prop 类型定义规范 (runtime vs type-only)、slot 模式、v-model 双向绑定',
61
+ knowledgeTypes: ['code-pattern', 'code-standard'],
62
+ skillWorthy: true,
63
+ dualOutput: true,
64
+ skillMeta: {
65
+ name: 'project-vue-component-api',
66
+ description:
67
+ 'Vue component API conventions — defineProps/defineEmits/defineModel patterns (auto-generated by enhancement)',
68
+ },
69
+ },
34
70
  ];
35
71
  }
36
72
 
@@ -38,6 +74,8 @@ class VueEnhancement extends EnhancementPack {
38
74
  return [
39
75
  {
40
76
  ruleId: 'vue-no-v-html',
77
+ category: 'safety',
78
+ dimension: 'file',
41
79
  severity: 'warning',
42
80
  languages: ['typescript', 'javascript'],
43
81
  pattern: /v-html/,
@@ -45,11 +83,49 @@ class VueEnhancement extends EnhancementPack {
45
83
  },
46
84
  {
47
85
  ruleId: 'vue-composable-naming',
86
+ category: 'style',
87
+ dimension: 'file',
48
88
  severity: 'info',
49
89
  languages: ['typescript', 'javascript'],
50
90
  pattern: /export\s+function\s+(?!use)[a-z]/,
51
91
  message: 'Composable 函数建议以 use 前缀命名',
52
92
  },
93
+ {
94
+ ruleId: 'vue-reactive-destructure',
95
+ category: 'correctness',
96
+ dimension: 'file',
97
+ severity: 'warning',
98
+ languages: ['typescript', 'javascript'],
99
+ pattern: /(?:const|let)\s*\{[^}]+\}\s*=\s*(?:reactive|toRefs)\s*\(/,
100
+ message: '解构 reactive() 对象会失去响应性,使用 toRefs() 或保持引用访问。Vue 3.5 的 Reactive Props Destructure 除外',
101
+ },
102
+ {
103
+ ruleId: 'vue-computed-side-effect',
104
+ category: 'correctness',
105
+ dimension: 'file',
106
+ severity: 'warning',
107
+ languages: ['typescript', 'javascript'],
108
+ pattern: /computed\s*\(\s*(?:\(\)|function\s*\(\))\s*(?:=>)?\s*\{[^}]*(?:fetch|axios|console\.log|\.value\s*=)/,
109
+ message: 'computed 中不应有副作用 (网络请求、修改 ref、console.log) — 使用 watch/watchEffect 代替',
110
+ },
111
+ {
112
+ ruleId: 'vue-v-for-with-v-if',
113
+ category: 'performance',
114
+ dimension: 'file',
115
+ severity: 'warning',
116
+ languages: ['typescript', 'javascript'],
117
+ pattern: /v-for=[\s\S]*?v-if=/,
118
+ message: '避免在同一元素上同时使用 v-for 和 v-if — v-if 优先级高于 v-for(Vue 3),用 computed 过滤或包裹 <template>',
119
+ },
120
+ {
121
+ ruleId: 'vue-no-ref-in-reactive',
122
+ category: 'correctness',
123
+ dimension: 'file',
124
+ severity: 'info',
125
+ languages: ['typescript', 'javascript'],
126
+ pattern: /reactive\s*\(\s*\{[^}]*ref\s*\(/,
127
+ message: '避免在 reactive() 内嵌套 ref() — reactive 会自动解包 ref,但手动嵌套会造成混淆',
128
+ },
53
129
  ];
54
130
  }
55
131
 
@@ -79,6 +155,8 @@ class VueEnhancement extends EnhancementPack {
79
155
 
80
156
  detectPatterns(astSummary) {
81
157
  const patterns = [];
158
+
159
+ // ── Composable functions ──
82
160
  for (const m of astSummary.methods || []) {
83
161
  if (m.name && /^use[A-Z]/.test(m.name) && !m.className) {
84
162
  patterns.push({
@@ -89,6 +167,69 @@ class VueEnhancement extends EnhancementPack {
89
167
  });
90
168
  }
91
169
  }
170
+
171
+ // ── Pinia stores (defineStore) ──
172
+ for (const m of astSummary.methods || []) {
173
+ if (m.name && /^use\w+Store$/.test(m.name) && !m.className) {
174
+ patterns.push({
175
+ type: 'vue-pinia-store',
176
+ methodName: m.name,
177
+ line: m.line,
178
+ confidence: 0.9,
179
+ });
180
+ }
181
+ }
182
+
183
+ // ── Vue Router guard patterns ──
184
+ for (const m of astSummary.methods || []) {
185
+ if (!m.className && m.name) {
186
+ const nameLower = m.name.toLowerCase();
187
+ if (
188
+ nameLower.includes('beforeeach') ||
189
+ nameLower.includes('beforeenter') ||
190
+ nameLower.includes('beforerouteleave') ||
191
+ nameLower === 'beforeRouteEnter' ||
192
+ nameLower === 'beforeRouteUpdate'
193
+ ) {
194
+ patterns.push({
195
+ type: 'vue-router-guard',
196
+ methodName: m.name,
197
+ line: m.line,
198
+ confidence: 0.85,
199
+ });
200
+ }
201
+ }
202
+ }
203
+
204
+ // ── Plugin install functions ──
205
+ for (const m of astSummary.methods || []) {
206
+ if (m.isExported && m.name === 'install' && !m.className) {
207
+ patterns.push({
208
+ type: 'vue-plugin',
209
+ methodName: m.name,
210
+ line: m.line,
211
+ confidence: 0.85,
212
+ });
213
+ }
214
+ }
215
+
216
+ // ── Vue ecosystem imports ──
217
+ const vueImports = (astSummary.imports || []).filter(
218
+ (imp) =>
219
+ imp.includes('vue') ||
220
+ imp.includes('pinia') ||
221
+ imp.includes('vue-router') ||
222
+ imp.includes('vueuse') ||
223
+ imp.includes('@vueuse')
224
+ );
225
+ if (vueImports.length > 0) {
226
+ patterns.push({
227
+ type: 'vue-ecosystem-usage',
228
+ importCount: vueImports.length,
229
+ confidence: 0.85,
230
+ });
231
+ }
232
+
92
233
  return patterns;
93
234
  }
94
235
  }