@devflow-tools/plugin-taro 0.12.6 → 0.13.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/dist/index.js ADDED
@@ -0,0 +1,1317 @@
1
+ const structuredOutputSchema = {
2
+ type: "object",
3
+ additionalProperties: true,
4
+ };
5
+ const readOnlyAnnotations = {
6
+ readOnlyHint: true,
7
+ idempotentHint: true,
8
+ };
9
+ export const taroPlugin = {
10
+ meta: {
11
+ name: "taro",
12
+ version: "0.1.0",
13
+ description: "Taro 跨端专家 — 多端适配、小程序调试、性能优化、兼容方案",
14
+ icon: "🛠️",
15
+ tags: ["frontend", "taro", "miniapp", "cross-platform"],
16
+ },
17
+ knowledge: {
18
+ sources: [
19
+ {
20
+ name: "taro-docs",
21
+ urls: [
22
+ "https://docs.taro.zone/docs/",
23
+ "https://docs.taro.zone/docs/apis/",
24
+ "https://docs.taro.zone/docs/components-desc",
25
+ ],
26
+ pluginName: "taro",
27
+ version: "3.6.0",
28
+ renderJs: true,
29
+ },
30
+ ],
31
+ },
32
+ memory: {
33
+ project: {
34
+ stack: ["Taro 3", "React", "TypeScript", "小程序"],
35
+ conventions: [
36
+ { key: "platform", value: "使用 process.env.TARO_ENV 判断平台,避免 DOM API", source: "manual", confidence: 0.95 },
37
+ { key: "components", value: "优先使用 Taro 内置组件(View、Text、Image)", source: "manual", confidence: 0.9 },
38
+ { key: "styling", value: "样式单位使用 px 自动转换,border-box 盒模型", source: "manual", confidence: 0.8 },
39
+ { key: "api", value: "网络请求使用 Taro.request,存储使用 Taro.setStorage", source: "manual", confidence: 0.9 },
40
+ ],
41
+ },
42
+ global: [
43
+ {
44
+ id: "taro:cross-platform",
45
+ source: "taro",
46
+ title: "跨端适配策略",
47
+ content: "支持微信/支付宝/百度/字节小程序 + H5 + RN。使用 process.env.TARO_ENV 判断平台。条件编译处理差异。使用 Taro 内置组件确保跨端一致性。",
48
+ embedding: [],
49
+ },
50
+ {
51
+ id: "taro:performance",
52
+ source: "taro",
53
+ title: "小程序性能优化",
54
+ content: "使用 VirtualList 渲染长列表。图片懒加载。setState 批量更新避免频繁渲染。分包加载减少首屏体积。页面预加载使用 Taro.preload。",
55
+ embedding: [],
56
+ },
57
+ {
58
+ id: "taro:pitfalls",
59
+ source: "taro",
60
+ title: "跨端常见坑点",
61
+ content: "小程序无 DOM API(window/document)。CSS 不支持 vh/vw。不支持 SVG。主包 2MB 限制,分包 20MB。图片上 CDN。生命周期差异:useDidShow/useDidHide。",
62
+ embedding: [],
63
+ },
64
+ {
65
+ id: "taro:platform-diff",
66
+ source: "taro",
67
+ title: "平台 API 差异速查",
68
+ content: "微信支付用 wx.requestPayment,支付宝用 my.tradePay。地图组件微信用 map,支付宝用 map 但属性不同。获取用户信息各平台 API 不同。条件和平台文件后缀区分。",
69
+ embedding: [],
70
+ },
71
+ ],
72
+ },
73
+ workflow: {
74
+ steps: [],
75
+ templates: [
76
+ // ── 新建页面 ──
77
+ {
78
+ name: "new-page",
79
+ description: "新建 Taro 页面(跨端兼容 + 路由注册 + 分包配置)",
80
+ version: "2.0.0",
81
+ steps: [
82
+ {
83
+ id: "analyze-platform-needs",
84
+ title: "分析平台和功能需求",
85
+ instruction: [
86
+ "## 分析需要支持的平台和差异化需求",
87
+ "",
88
+ "明确页面需要支持的目标平台,以及是否存在平台差异化逻辑。",
89
+ "",
90
+ "**执行动作**:",
91
+ "1. 读取项目 `config/` 或 `project.config.json` 确定目标平台:",
92
+ " ```bash",
93
+ " cat config/index.ts 2>/dev/null | head -80",
94
+ " cat project.config.json 2>/dev/null | head -40",
95
+ " ```",
96
+ "2. 询问/确认页面需要支持的平台:",
97
+ " - weapp(微信小程序)、alipay(支付宝小程序)",
98
+ " - h5(浏览器)、rn(React Native)、swan(百度小程序)、tt(字节小程序)",
99
+ "3. 判断页面是否存在平台差异化逻辑:",
100
+ " - 是否用到支付、地图、分享等原生能力?",
101
+ " - 是否用到平台特有的 UI 组件?",
102
+ " - 是否需要不同平台的数据持久化方式?",
103
+ "4. 确定是否需要分包加载:",
104
+ " ```bash",
105
+ " grep -A 30 'subPackages\\|subpackages' src/app.config.ts 2>/dev/null",
106
+ " ```",
107
+ "5. 确认页面类型:TabBar 页(必须放主包)还是普通页(可放分包)",
108
+ "",
109
+ "**产出格式**:",
110
+ "```",
111
+ "page_name: <page-name>",
112
+ "target_platforms: ['weapp', 'h5', 'alipay']",
113
+ "needs_conditional_compile: true | false",
114
+ "platform_diffs: [{ feature, platforms }]",
115
+ "should_be_subpackage: true | false",
116
+ "subpackage_root: <root | null>",
117
+ "```",
118
+ ].join("\n"),
119
+ suggestedTools: ["Read", "Bash"],
120
+ output: "平台、差异点和分包规划",
121
+ },
122
+ {
123
+ id: "create-page-component",
124
+ title: "创建页面组件",
125
+ instruction: [
126
+ "## 创建 Taro 页面组件(内置组件 + 条件编译)",
127
+ "",
128
+ "基于 `analyze-platform-needs` 的平台规划,创建页面组件。",
129
+ "",
130
+ "**执行动作**:",
131
+ "1. 创建页面目录和文件:",
132
+ " ```bash",
133
+ " mkdir -p src/pages/<page-name>/",
134
+ " # 如果是分包内:mkdir -p src/pages/<subpackage-root>/<page-name>/",
135
+ " ```",
136
+ "2. 创建主文件 `index.tsx`,使用 Taro 内置组件:",
137
+ " ```typescript",
138
+ " import { View, Text, Image } from '@tarojs/components';",
139
+ " import Taro, { useLoad, useReady } from '@tarojs/taro';",
140
+ " import './index.scss';",
141
+ "",
142
+ " export default function <PageName>() {",
143
+ " useLoad(() => {",
144
+ " console.log('Page loaded.');",
145
+ " });",
146
+ " useReady(() => {",
147
+ " console.log('Page ready.');",
148
+ " });",
149
+ "",
150
+ " return (",
151
+ " <View className='page'>",
152
+ " <Text>页面标题</Text>",
153
+ " </View>",
154
+ " );",
155
+ " }",
156
+ " ```",
157
+ "3. 如需平台差异化,使用条件编译:",
158
+ " ```typescript",
159
+ " if (process.env.TARO_ENV === 'weapp') {",
160
+ " Taro.navigateTo({ url: '/pages/weapp-only/detail' });",
161
+ " } else if (process.env.TARO_ENV === 'h5') {",
162
+ " window.location.href = '/detail';",
163
+ " }",
164
+ " ```",
165
+ "4. 或使用平台文件后缀:创建 `index.weapp.tsx` 和 `index.h5.tsx`",
166
+ "5. 使用 `Taro.xxx` API 替代浏览器 API:",
167
+ " - `Taro.request` 替代 `fetch`/`axios`",
168
+ " - `Taro.getStorageSync` 替代 `localStorage`",
169
+ " - `Taro.navigateTo` 替代 `window.location`",
170
+ "",
171
+ "**产出格式**:",
172
+ "```",
173
+ "page_files: [{ path: 'src/pages/<name>/index.tsx' }]",
174
+ "uses_conditional_compile: true | false",
175
+ "conditional_branches: ['weapp', 'h5', ...]",
176
+ "```",
177
+ ].join("\n"),
178
+ suggestedTools: ["Bash", "Read", "Write"],
179
+ output: "创建的页面组件文件",
180
+ depends: ["analyze-platform-needs"],
181
+ },
182
+ {
183
+ id: "check-compatibility",
184
+ title: "检查跨端兼容性",
185
+ instruction: [
186
+ "## 检查使用了仅 Web/仅小程序 API",
187
+ "",
188
+ "扫描新创建的页面,识别跨端不兼容的 API 调用。",
189
+ "",
190
+ "**执行动作**:",
191
+ "1. 检查 DOM API 使用:",
192
+ " ```bash",
193
+ " grep -rn 'window\\.\\|document\\.\\|localStorage\\|sessionStorage' src/pages/<page-name>/",
194
+ " ```",
195
+ "2. 检查 HTML 标签使用(小程序应使用 Taro 组件):",
196
+ " ```bash",
197
+ " grep -rn '<div\\|<span\\|<img\\|<a href\\|<p>' src/pages/<page-name>/ --include='*.tsx' --include='*.jsx'",
198
+ " ```",
199
+ "3. 检查浏览器专属 API:",
200
+ " ```bash",
201
+ " grep -rn 'fetch(\\|XMLHttpRequest\\|navigator\\.\\|location\\.\\|history\\.' src/pages/<page-name>/",
202
+ " ```",
203
+ "4. 检查 CSS 不兼容单位:",
204
+ " ```bash",
205
+ " grep -rn 'vh\\|vw\\|rem' src/pages/<page-name>/ --include='*.scss' --include='*.css' --include='*.less'",
206
+ " ```",
207
+ "5. 检查使用了 SVG(小程序不支持):",
208
+ " ```bash",
209
+ " grep -rn '<svg\\|\\.svg' src/pages/<page-name>/",
210
+ " ```",
211
+ "6. 如有发现,给出替换方案:",
212
+ " - `window`/`document` → 删除或条件编译",
213
+ " - `<div>` → `<View>`,`<span>` → `<Text>`,`<img>` → `<Image>`",
214
+ " - `fetch` → `Taro.request`",
215
+ " - `localStorage` → `Taro.getStorageSync`/`Taro.setStorageSync`",
216
+ "",
217
+ "**产出格式**:",
218
+ "```",
219
+ "compat_issues: [{",
220
+ " file: '...',",
221
+ " line: N,",
222
+ " usage: 'localStorage.setItem',",
223
+ " replacement: 'Taro.setStorageSync',",
224
+ " severity: 'error' | 'warning'",
225
+ "}]",
226
+ "```",
227
+ ].join("\n"),
228
+ suggestedTools: ["Bash", "Read"],
229
+ output: "兼容性扫描结果(DOM API、HTML 标签、浏览器 API)",
230
+ depends: ["create-page-component"],
231
+ },
232
+ {
233
+ id: "config-route",
234
+ title: "在 app.config.ts 注册路由",
235
+ instruction: [
236
+ "## 在 app.config.ts 中注册路由",
237
+ "",
238
+ "将新页面注册到 Taro 路由配置中。",
239
+ "",
240
+ "**执行动作**:",
241
+ "1. 打开 `src/app.config.ts`:",
242
+ " ```bash",
243
+ " cat src/app.config.ts 2>/dev/null || cat src/app.config.js 2>/dev/null",
244
+ " ```",
245
+ "2. 在 `pages` 数组中添加新页面路径:",
246
+ " ```typescript",
247
+ " export default defineAppConfig({",
248
+ " pages: [",
249
+ " 'pages/index/index',",
250
+ " // 新增页面路径(注意:不带 .tsx 后缀)",
251
+ " 'pages/<page-name>/index',",
252
+ " ],",
253
+ " });",
254
+ " ```",
255
+ "3. 如果页面是 TabBar 页面,还需配置 `tabBar.list`:",
256
+ " ```typescript",
257
+ " tabBar: {",
258
+ " list: [",
259
+ " { pagePath: 'pages/<page-name>/index', text: '页面名称' },",
260
+ " ],",
261
+ " }",
262
+ " ```",
263
+ "4. 如果页面是分包页面(在 `config-subpackage` 配置),则**不要**加到 `pages` 中,而是加到 `subPackages` 里",
264
+ "",
265
+ "**产出格式**:",
266
+ "```",
267
+ "route_added: 'pages/<page-name>/index'",
268
+ "is_tabbar: true | false",
269
+ "belongs_to: 'main' | '<subpackage-root>'",
270
+ "```",
271
+ ].join("\n"),
272
+ suggestedTools: ["Read", "Edit"],
273
+ output: "在 app.config.ts 注册的路由条目",
274
+ depends: ["create-page-component"],
275
+ },
276
+ {
277
+ id: "config-subpackage",
278
+ title: "配置分包(如需)",
279
+ instruction: [
280
+ "## 配置分包(如页面属于非主包模块)",
281
+ "",
282
+ "如 `analyze-platform-needs` 判定页面应放在分包,将其加入分包配置。",
283
+ "",
284
+ "**执行动作**:",
285
+ "1. 打开 `src/app.config.ts`,定位 `subPackages`:",
286
+ " ```bash",
287
+ " grep -n 'subPackages\\|subpackages' src/app.config.ts",
288
+ " ```",
289
+ "2. 如果分包已存在,将页面追加到对应分包的 `pages` 数组:",
290
+ " ```typescript",
291
+ " subPackages: [",
292
+ " {",
293
+ " root: 'packageA',",
294
+ " pages: [",
295
+ " 'pages/<page-name>/index',",
296
+ " ],",
297
+ " },",
298
+ " ]",
299
+ " ```",
300
+ "3. 如果分包不存在,新建一个分包:",
301
+ " ```typescript",
302
+ " subPackages: [",
303
+ " {",
304
+ " root: 'packageA',",
305
+ " name: 'packageA',",
306
+ " pages: ['pages/<page-name>/index'],",
307
+ " },",
308
+ " ]",
309
+ " ```",
310
+ "4. 可选:配置 `preloadRule` 实现分包预下载:",
311
+ " ```typescript",
312
+ " preloadRule: {",
313
+ " 'pages/index/index': {",
314
+ " network: 'all',",
315
+ " packages: ['packageA'],",
316
+ " },",
317
+ " }",
318
+ " ```",
319
+ "5. 确认页面文件实际路径与分包 `root` + `pages` 条目对应:",
320
+ " ```bash",
321
+ " ls src/<subpackage-root>/pages/<page-name>/",
322
+ " ```",
323
+ "",
324
+ "**产出格式**:",
325
+ "```",
326
+ "subpackage_root: 'packageA'",
327
+ "subpackage_pages_added: ['pages/<page-name>/index']",
328
+ "preload_rule_added: true | false",
329
+ "```",
330
+ ].join("\n"),
331
+ suggestedTools: ["Read", "Edit"],
332
+ output: "分包配置更新结果",
333
+ depends: ["config-route"],
334
+ },
335
+ {
336
+ id: "create-styles",
337
+ title: "创建跨端样式",
338
+ instruction: [
339
+ "## 创建跨端样式(px 单位 + scoped)",
340
+ "",
341
+ "为页面创建样式文件,使用 Taro 自动转换的 px 单位。",
342
+ "",
343
+ "**执行动作**:",
344
+ "1. 创建样式文件 `index.scss`(或 `.less`,与项目保持一致):",
345
+ " ```bash",
346
+ " ls src/pages/<page-name>/",
347
+ " ```",
348
+ "2. 使用 px 单位,Taro 会自动转为小程序的 rpx:",
349
+ " ```scss",
350
+ " .page {",
351
+ " padding: 32px; // 编译后变成 32rpx",
352
+ " background: #fff;",
353
+ "",
354
+ " .title {",
355
+ " font-size: 36px;",
356
+ " font-weight: 600;",
357
+ " color: #333;",
358
+ " }",
359
+ " }",
360
+ " ```",
361
+ "3. 避免使用小程序不支持的 CSS 特性:",
362
+ " - 不要用 `vh`/`vw`(部分小程序不支持),改用 px 或百分比",
363
+ " - 不要用 CSS 动画以外的 SVG",
364
+ " - 盒模型使用 `border-box`",
365
+ "4. 如需平台差异化样式,使用平台样式后缀:",
366
+ " - `index.weapp.scss`、`index.h5.scss`",
367
+ " - 或在文件中用 CSS 变量区分",
368
+ "5. 图片使用 `background-image` + CDN 地址,不要打包到主包",
369
+ "",
370
+ "**产出格式**:",
371
+ "```",
372
+ "style_files: ['src/pages/<page-name>/index.scss']",
373
+ "platform_specific_styles: ['weapp' | 'h5' | null]",
374
+ "```",
375
+ ].join("\n"),
376
+ suggestedTools: ["Bash", "Write", "Read"],
377
+ output: "创建的页面样式文件",
378
+ depends: ["create-page-component"],
379
+ },
380
+ { id: "build-verify", stepTemplate: "build-verify", depends: ["check-compatibility", "create-styles"] },
381
+ ],
382
+ triggers: [{ type: "cli", command: "new:page" }, { type: "mcp", tool: "new_page" }],
383
+ },
384
+ // ── 跨端兼容检查 ──
385
+ {
386
+ name: "cross-platform-check",
387
+ description: "跨端兼容检查:扫描平台 API → 识别风险点 → 建议兼容方案",
388
+ version: "2.0.0",
389
+ steps: [
390
+ {
391
+ id: "scan-platform-apis",
392
+ title: "扫描平台特定 API",
393
+ instruction: [
394
+ "## 扫描代码中使用的平台特定 API",
395
+ "",
396
+ "扫描项目源码,识别所有涉及平台差异化或原生能力的 API 调用。",
397
+ "",
398
+ "**执行动作**:",
399
+ "1. 扫描 `Taro.` API 调用分布:",
400
+ " ```bash",
401
+ " grep -rn 'Taro\\.' src/ --include='*.ts' --include='*.tsx' | awk -F'Taro\\.' '{print $2}' | awk -F'(' '{print $1}' | sort | uniq -c | sort -rn",
402
+ " ```",
403
+ "2. 扫描微信支付、地图等原生能力 API:",
404
+ " ```bash",
405
+ " grep -rn 'requestPayment\\|openLocation\\|getLocation\\|chooseImage\\|login\\|getUserProfile\\|shareAppMessage' src/ --include='*.ts' --include='*.tsx'",
406
+ " ```",
407
+ "3. 扫描平台差异化写法(`TARO_ENV` 条件编译):",
408
+ " ```bash",
409
+ " grep -rn 'TARO_ENV' src/ --include='*.ts' --include='*.tsx'",
410
+ " ```",
411
+ "4. 扫描平台文件后缀(`.weapp.`、`.h5.`、`.alipay.` 等):",
412
+ " ```bash",
413
+ " find src/ -type f \\( -name '*.weapp.*' -o -name '*.h5.*' -o -name '*.alipay.*' -o -name '*.swan.*' -o -name '*.tt.*' -o -name '*.rn.*' \\)",
414
+ " ```",
415
+ "5. 整理 API 清单,标记每个 API 在各平台的支持情况",
416
+ "",
417
+ "**产出格式**:",
418
+ "```",
419
+ "taro_apis_used: [{ api: 'Taro.request', count: 20, platforms: ['weapp', 'h5', 'alipay'] }],",
420
+ "native_apis: [{ api: 'requestPayment', platforms: ['weapp'], need_polyfill: true }],",
421
+ "conditional_compiles: [{ file, line, branches: ['weapp', 'h5'] }],",
422
+ "platform_suffix_files: ['src/utils/request.weapp.ts', ...],",
423
+ "```",
424
+ ].join("\n"),
425
+ suggestedTools: ["Bash", "Read"],
426
+ output: "平台特定 API 使用清单",
427
+ },
428
+ {
429
+ id: "detect-dom-usage",
430
+ title: "检测 DOM API 使用",
431
+ instruction: [
432
+ "## 检测 window/document 等 DOM API 使用",
433
+ "",
434
+ "小程序环境没有 DOM/BOM API,必须识别并替换。",
435
+ "",
436
+ "**执行动作**:",
437
+ "1. 检测 `window` 相关 API:",
438
+ " ```bash",
439
+ " grep -rn 'window\\.\\|globalThis\\.' src/ --include='*.ts' --include='*.tsx'",
440
+ " ```",
441
+ "2. 检测 `document` 相关 API:",
442
+ " ```bash",
443
+ " grep -rn 'document\\.\\|document\\.body\\|document\\.createElement' src/ --include='*.ts' --include='*.tsx'",
444
+ " ```",
445
+ "3. 检测 `localStorage`/`sessionStorage`:",
446
+ " ```bash",
447
+ " grep -rn 'localStorage\\|sessionStorage' src/ --include='*.ts' --include='*.tsx'",
448
+ " ```",
449
+ "4. 检测 `navigator` 和 `location`:",
450
+ " ```bash",
451
+ " grep -rn 'navigator\\.\\|location\\.href\\|location\\.reload' src/ --include='*.ts' --include='*.tsx'",
452
+ " ```",
453
+ "5. 检测 `fetch`、`XMLHttpRequest`、`WebSocket`(部分小程序支持):",
454
+ " ```bash",
455
+ " grep -rn 'fetch(\\|XMLHttpRequest\\|WebSocket' src/ --include='*.ts' --include='*.tsx'",
456
+ " ```",
457
+ "6. 检测 HTML 标签(应替换为 Taro 组件):",
458
+ " ```bash",
459
+ " grep -rn '<div\\|<span\\|<img\\|<a href\\|<input\\|<button' src/ --include='*.tsx' --include='*.jsx' -l",
460
+ " ```",
461
+ "7. 对每处使用,给出替换建议:",
462
+ " - `localStorage` → `Taro.getStorageSync`/`Taro.setStorageSync`",
463
+ " - `fetch` → `Taro.request`",
464
+ " - `<div>` → `<View>`,`<img>` → `<Image>`,`<a>` → `<Navigator>` 或 `Taro.navigateTo`",
465
+ " - `window.location` → `Taro.navigateTo`/`Taro.switchTab`",
466
+ "",
467
+ "**产出格式**:",
468
+ "```",
469
+ "dom_issues: [{",
470
+ " file: '...',",
471
+ " line: N,",
472
+ " api: 'localStorage.setItem',",
473
+ " replacement: 'Taro.setStorageSync',",
474
+ " severity: 'error' | 'warning'",
475
+ "}]",
476
+ "```",
477
+ ].join("\n"),
478
+ suggestedTools: ["Bash", "Read"],
479
+ output: "DOM API 使用位置清单和替换建议",
480
+ },
481
+ {
482
+ id: "check-conditional-compile",
483
+ title: "检查条件编译覆盖",
484
+ instruction: [
485
+ "## 检查条件编译是否覆盖所有目标平台",
486
+ "",
487
+ "基于 `scan-platform-apis` 扫描到的差异点,检查条件编译的完整性。",
488
+ "",
489
+ "**执行动作**:",
490
+ "1. 列出所有使用 `process.env.TARO_ENV` 的位置:",
491
+ " ```bash",
492
+ " grep -rn 'TARO_ENV' src/ --include='*.ts' --include='*.tsx' -B 1 -A 3",
493
+ " ```",
494
+ "2. 对每个条件分支,检查是否覆盖所有目标平台:",
495
+ " - 目标平台见 `config/index.ts` 的 `projects` 或 `designWidth` 配置",
496
+ " - 检查是否有 `else` 兜底分支",
497
+ " - 检查是否漏掉了 `alipay`/`swan`/`tt` 等平台",
498
+ "3. 检查平台文件后缀是否配套:",
499
+ " - 如果存在 `.weapp.ts`,是否同时存在 `.h5.ts`、`.alipay.ts` 等对应文件",
500
+ " ```bash",
501
+ " find src/ -name '*.weapp.*' | sed 's/\\.weapp\\.//' | sort > /tmp/weapp",
502
+ " find src/ -name '*.h5.*' | sed 's/\\.h5\\.//' | sort > /tmp/h5",
503
+ " comm -23 /tmp/weapp /tmp/h5 # 列出仅在 weapp 存在的文件",
504
+ " ```",
505
+ "4. 检查是否所有平台差异分支都有测试或文档说明",
506
+ "",
507
+ "**产出格式**:",
508
+ "```",
509
+ "conditional_compile_coverage: [{",
510
+ " file: '...',",
511
+ " line: N,",
512
+ " branches_present: ['weapp', 'h5'],",
513
+ " branches_missing: ['alipay'],",
514
+ " has_else_fallback: true | false",
515
+ "}],",
516
+ "orphan_platform_files: ['src/utils/x.weapp.ts (no .h5.ts counterpart)'],",
517
+ "```",
518
+ ].join("\n"),
519
+ suggestedTools: ["Bash", "Read"],
520
+ output: "条件编译覆盖分析结果",
521
+ depends: ["scan-platform-apis"],
522
+ },
523
+ {
524
+ id: "check-component-compat",
525
+ title: "检查组件跨端兼容",
526
+ instruction: [
527
+ "## 检查组件在各平台是否有等效实现",
528
+ "",
529
+ "确认项目中使用的组件(含三方库)在所有目标平台都有等效实现。",
530
+ "",
531
+ "**执行动作**:",
532
+ "1. 列出所有使用的三方 UI 库:",
533
+ " ```bash",
534
+ " grep -E '\"@taroify|\"taro-|\"@ant-design|\"antd-mobile|\"@nutui' package.json",
535
+ " ```",
536
+ "2. 检查三方库是否支持所有目标平台:",
537
+ " - 查阅三方库文档的「跨端支持」章节",
538
+ " - 常见库:`@taroify/*` 全平台,`@nutui/*` 小程序+H5,部分库只支持 H5",
539
+ "3. 检查项目中是否使用了自定义 Web Components:",
540
+ " ```bash",
541
+ " grep -rn 'customElements\\|HTMLElement\\|class.*extends.*Element' src/ --include='*.ts' --include='*.tsx'",
542
+ " ```",
543
+ "4. 检查 `<Canvas>`、`<Map>`、`<Video>`、`<Camera>` 等原生组件在各平台的行为差异:",
544
+ " ```bash",
545
+ " grep -rn '<Canvas\\|<Map\\|<Video\\|<Camera\\|<WebView' src/ --include='*.tsx' --include='*.jsx'",
546
+ " ```",
547
+ "5. 检查使用了 `@tarojs/components` 中哪些组件,对照官方文档确认平台支持:",
548
+ " ```bash",
549
+ " grep -rn \"from '@tarojs/components'\" src/ -h | sort -u",
550
+ " ```",
551
+ "6. 对不兼容的组件给出替换方案",
552
+ "",
553
+ "**产出格式**:",
554
+ "```",
555
+ "third_party_libs: [{ name: '...', supports: ['weapp', 'h5'], issues: '...' }],",
556
+ "native_components: [{ name: '<Canvas>', platform_issues: [{ platform: 'alipay', issue: '...' }] }],",
557
+ "taro_components_used: ['View', 'Text', 'Image', ...],",
558
+ "compat_warnings: [{ component, platforms_unsupported: [...] }],",
559
+ "```",
560
+ ].join("\n"),
561
+ suggestedTools: ["Bash", "Read"],
562
+ output: "组件跨端兼容性分析结果",
563
+ },
564
+ { id: "generate-report", stepTemplate: "generate-report", depends: ["scan-platform-apis", "detect-dom-usage", "check-component-compat"] },
565
+ {
566
+ id: "apply-compat-fixes",
567
+ title: "应用兼容修复",
568
+ instruction: [
569
+ "## 应用兼容修复(条件编译 + 平台文件后缀)",
570
+ "",
571
+ "基于 `generate-report` 的发现,应用跨端兼容修复。",
572
+ "",
573
+ "**执行动作**:",
574
+ "1. 对 DOM API 使用,替换为 Taro API:",
575
+ " ```typescript",
576
+ " // 替换前",
577
+ " localStorage.setItem('key', value);",
578
+ " // 替换后",
579
+ " Taro.setStorageSync('key', value);",
580
+ " ```",
581
+ "2. 对 HTML 标签,替换为 Taro 组件:",
582
+ " - `<div>` → `<View>`, `<span>` → `<Text>`",
583
+ " - `<img>` → `<Image src={...} />`(注意属性名)",
584
+ " - `<a href>` → `<Navigator url={...}>` 或 `Taro.navigateTo`",
585
+ " - `<input>` → `<Input>`,`<button>` → `<Button>`",
586
+ "3. 对无法跨平台统一处理的差异,添加条件编译:",
587
+ " ```typescript",
588
+ " if (process.env.TARO_ENV === 'weapp') {",
589
+ " // 微信特定逻辑",
590
+ " } else if (process.env.TARO_ENV === 'h5') {",
591
+ " // H5 逻辑",
592
+ " }",
593
+ " ```",
594
+ "4. 对差异较大的组件/页面,使用平台文件后缀:",
595
+ " - 创建 `foo.weapp.tsx` 和 `foo.h5.tsx`",
596
+ " - 导入时只写 `import Foo from './foo'`,Taro 会自动选择平台文件",
597
+ "5. 为每个修复点验证:替换后不影响其他平台的构建",
598
+ "",
599
+ "**产出格式**:",
600
+ "```",
601
+ "fixes_applied: [{",
602
+ " file: '...',",
603
+ " change_type: 'replace-dom-api' | 'replace-html-tag' | 'add-conditional-compile' | 'split-platform-file',",
604
+ " before: 'localStorage.getItem(...)',",
605
+ " after: 'Taro.getStorageSync(...)',",
606
+ " platforms_affected: ['weapp', 'h5'],",
607
+ "}]",
608
+ "```",
609
+ ].join("\n"),
610
+ suggestedTools: ["Read", "Edit", "Write"],
611
+ output: "应用的兼容修复清单",
612
+ depends: ["generate-report"],
613
+ },
614
+ { id: "multi-platform-build", stepTemplate: "build-verify", depends: ["apply-compat-fixes"] },
615
+ ],
616
+ triggers: [{ type: "cli", command: "check:cross-platform" }],
617
+ },
618
+ // ── 小程序调试 ──
619
+ {
620
+ name: "mini-program-debug",
621
+ description: "小程序调试:错误排查 → 包体积分析 → 性能检测",
622
+ version: "2.0.0",
623
+ steps: [
624
+ {
625
+ id: "collect-logs",
626
+ title: "收集错误日志",
627
+ instruction: [
628
+ "## 收集小程序错误日志和控制台输出",
629
+ "",
630
+ "从小程序开发者工具、真机调试或日志平台收集错误信息。",
631
+ "",
632
+ "**执行动作**:",
633
+ "1. 收集开发者工具 Console 输出:",
634
+ " - 错误信息(红色)",
635
+ " - 警告信息(黄色)",
636
+ " - `console.log`/`console.error` 输出",
637
+ "2. 收集 AppEvent 错误日志:",
638
+ " ```bash",
639
+ " # 如果有日志文件",
640
+ " find . -name '*.log' -path '*/miniprogram/*' -o -name 'applog*' 2>/dev/null | head -10",
641
+ " ```",
642
+ "3. 检查错误类型:",
643
+ " - `TypeError`:API 调用错误、未定义变量",
644
+ " - `ReferenceError`:引用未定义变量/组件",
645
+ " - `SyntaxError`:语法错误、条件编译解析错误",
646
+ " - 业务错误:API 返回错误码(如 401、500)",
647
+ "4. 收集环境信息:",
648
+ " - 基础库版本:`Taro.getSystemInfoSync()` 返回的 `SDKVersion`",
649
+ " - 平台:微信 / 支付宝 / 百度 / 字节",
650
+ " - 设备型号和操作系统版本",
651
+ "5. 记录复现步骤:",
652
+ " - 触发页面、操作路径",
653
+ " - 是首次加载还是二次进入",
654
+ " - 是否必现",
655
+ "",
656
+ "**产出格式**:",
657
+ "```",
658
+ "errors: [{",
659
+ " type: 'TypeError' | 'ReferenceError' | 'api_error' | 'runtime',",
660
+ " message: '...',",
661
+ " stack: '...',",
662
+ " file: '...',",
663
+ " line: N,",
664
+ " reproduce_steps: ['1. 进入页面', '2. 点击按钮', ...],",
665
+ "}],",
666
+ "environment: { platform: 'weapp', sdk_version: '2.25.0', device: '...' },",
667
+ "```",
668
+ ].join("\n"),
669
+ suggestedTools: ["Bash", "Read"],
670
+ output: "收集的错误日志和环境信息",
671
+ },
672
+ {
673
+ id: "check-api-permission",
674
+ title: "检查 API 权限配置",
675
+ instruction: [
676
+ "## 检查 API 权限配置",
677
+ "",
678
+ "小程序很多 API 需要在 `app.config.ts` 声明权限,否则会调用失败。",
679
+ "",
680
+ "**执行动作**:",
681
+ "1. 打开 `src/app.config.ts` 检查 `permission` 字段:",
682
+ " ```bash",
683
+ " grep -A 20 'permission:' src/app.config.ts",
684
+ " ```",
685
+ "2. 检查是否声明了 `scope.xxx` 权限:",
686
+ " ```bash",
687
+ " grep -rn 'scope\\.userLocation\\|scope\\.record\\|scope\\.writePhotosAlbum\\|scope\\.camera\\|scope\\.userInfo' src/ --include='*.ts' --include='*.tsx'",
688
+ " ```",
689
+ "3. 对照日志中的 API 调用,确认是否缺少权限声明:",
690
+ " - `Taro.getLocation` → 需要 `permission.scope.userLocation`",
691
+ " - `Taro.chooseImage` + 保存到相册 → 需要 `scope.writePhotosAlbum`",
692
+ " - `Taro.getPhoneNumber` → 需要组件和后端配合",
693
+ "4. 检查小程序后台(mp 平台)是否已开通对应接口权限:",
694
+ " - 微信支付、获取手机号、地图等需要后台配置",
695
+ "5. 检查 `requiredPrivateInfos` 是否声明(隐私接口):",
696
+ " ```bash",
697
+ " grep -A 10 'requiredPrivateInfos' src/app.config.ts",
698
+ " ```",
699
+ "",
700
+ "**产出格式**:",
701
+ "```",
702
+ "permission_config: {",
703
+ " declared: ['scope.userLocation', ...],",
704
+ " missing: ['scope.writePhotosAlbum (used by chooseImage save)'],",
705
+ "},",
706
+ "requiredPrivateInfos_declared: ['getLocation', ...],",
707
+ "backend_setup_needed: ['微信支付', ...],",
708
+ "```",
709
+ ].join("\n"),
710
+ suggestedTools: ["Read", "Bash"],
711
+ output: "API 权限配置检查结果",
712
+ },
713
+ {
714
+ id: "check-package-size",
715
+ title: "分析包体积",
716
+ instruction: [
717
+ "## 分析包体积(主包 + 分包 + 静态资源)",
718
+ "",
719
+ "小程序主包限制 2MB,总包 20MB,需要分析体积分布。",
720
+ "",
721
+ "**执行动作**:",
722
+ "1. 查看构建产物目录:",
723
+ " ```bash",
724
+ " du -sh dist/ 2>/dev/null",
725
+ " du -sh dist/* 2>/dev/null | sort -rh | head -20",
726
+ " ```",
727
+ "2. 分析主包大小:",
728
+ " ```bash",
729
+ " # 主包包含 app.json、app.js、app.wxss 和 pages/ 下的页面",
730
+ " du -sh dist/app.* 2>/dev/null",
731
+ " find dist/ -maxdepth 1 -type f -exec du -sh {} \\; 2>/dev/null",
732
+ " ```",
733
+ "3. 查找大文件(图片、字体、JSON 等):",
734
+ " ```bash",
735
+ " find dist/ -type f -size +100k -exec ls -lh {} \\; 2>/dev/null | sort -k5 -rh | head -20",
736
+ " ```",
737
+ "4. 检查分包配置是否合理:",
738
+ " ```bash",
739
+ " grep -A 20 'subPackages' src/app.config.ts",
740
+ " ```",
741
+ "5. 检查依赖包大小:",
742
+ " ```bash",
743
+ " du -sh node_modules/* 2>/dev/null | sort -rh | head -10",
744
+ " ```",
745
+ "6. 检查是否有无用文件被打入(test、docs、README):",
746
+ " ```bash",
747
+ " find dist/ -type f \\( -name 'README*' -o -name 'CHANGELOG*' -o -name '*.test.*' -o -name '*.spec.*' \\)",
748
+ " ```",
749
+ "",
750
+ "**产出格式**:",
751
+ "```",
752
+ "total_size: '1.8MB',",
753
+ "main_package_size: '1.95MB (接近 2MB 限制)',",
754
+ "subpackage_sizes: [{ name: 'packageA', size: '500KB' }],",
755
+ "large_files: [{ path: 'dist/images/bg.png', size: '450KB' }],",
756
+ "recommendations: ['图片迁移到 CDN', '使用分包加载', ...],",
757
+ "```",
758
+ ].join("\n"),
759
+ suggestedTools: ["Bash", "Read"],
760
+ output: "包体积分析结果",
761
+ depends: ["collect-logs"],
762
+ },
763
+ {
764
+ id: "check-lifecycle",
765
+ title: "检查页面生命周期",
766
+ instruction: [
767
+ "## 检查页面生命周期调用顺序",
768
+ "",
769
+ "小程序有独立的生命周期,与 React 生命周期有差异,混用会导致 Bug。",
770
+ "",
771
+ "**执行动作**:",
772
+ "1. 扫描生命周期 Hook 的使用:",
773
+ " ```bash",
774
+ " grep -rn 'useLoad\\|useShow\\|useReady\\|useHide\\|useUnload\\|usePullDownRefresh\\|useReachBottom\\|usePageScroll\\|useDidShow\\|useDidHide' src/ --include='*.tsx' --include='*.ts'",
775
+ " ```",
776
+ "2. 检查是否正确区分小程序生命周期和 React 生命周期:",
777
+ " - 小程序:`onLoad` → `onShow` → `onReady` → `onHide` → `onUnload`",
778
+ " - 对应 Taro Hooks:`useLoad`, `useShow`, `useReady`, `useHide`, `useUnload`",
779
+ " - React 的 `useEffect`/`useLayoutEffect` 在小程序语义不同",
780
+ "3. 常见问题检查:",
781
+ " - 在 `useEffect(() => [], [])` 中做页面初始化(应用 `useLoad`)",
782
+ " - 在 `componentDidMount` 获取页面参数(应用 `useLoad(options)` 拿 `options`)",
783
+ " - 页面返回时刷新数据(应用 `useShow` 而非 `useLoad`)",
784
+ "4. 检查页面参数获取方式:",
785
+ " ```bash",
786
+ " grep -rn 'getCurrentInstance\\|Taro.getCurrentInstance\\|useRouter\\|onLoad(options)' src/ --include='*.tsx' --include='*.ts'",
787
+ " ```",
788
+ "5. 检查生命周期顺序依赖:",
789
+ " - `useLoad` 在页面加载时触发一次",
790
+ " - `useShow` 每次页面显示都触发(包括从其他页面返回)",
791
+ " - `useReady` 在页面初次渲染完成时触发",
792
+ "",
793
+ "**产出格式**:",
794
+ "```",
795
+ "lifecycle_usage: [{",
796
+ " file: '...',",
797
+ " hooks_used: ['useEffect', 'useLoad'],",
798
+ " issues: ['should use useLoad instead of useEffect for page init']",
799
+ "}],",
800
+ "page_params_access: 'getCurrentInstance | useRouter | onLoad options',",
801
+ "```",
802
+ ].join("\n"),
803
+ suggestedTools: ["Bash", "Read"],
804
+ output: "生命周期使用分析",
805
+ depends: ["collect-logs"],
806
+ },
807
+ {
808
+ id: "search-docs",
809
+ title: "搜索 Taro 文档",
810
+ instruction: [
811
+ "## 搜索 Taro 文档中相关问题",
812
+ "",
813
+ "基于前面步骤收集到的错误和信息,搜索官方文档寻找解决方案。",
814
+ "",
815
+ "**执行动作**:",
816
+ "1. 提取关键错误信息:",
817
+ " - API 错误:如 `getLocation:fail auth deny`",
818
+ " - 渲染错误:如 `Component is not defined`",
819
+ " - 构建错误:如 `Module not found`",
820
+ "2. 查阅 Taro 官方文档:",
821
+ " - 主站:https://docs.taro.zone/docs/",
822
+ " - API:https://docs.taro.zone/docs/apis/",
823
+ " - 组件:https://docs.taro.zone/docs/components-desc",
824
+ " - 跨端常见问题:https://docs.taro.zone/docs/before-dev-remind",
825
+ "3. 使用 devflow 知识库查询:",
826
+ " ```bash",
827
+ " # 关键词: taro <API 名>, taro <错误类型>, 小程序 <问题>",
828
+ " ```",
829
+ "4. 查阅 GitHub Issues:",
830
+ " ```bash",
831
+ " # 使用 gh 命令搜索 nervjs/taro 仓库",
832
+ " # gh issue list --repo nervjs/taro --search '<keyword>' --state all --limit 10",
833
+ " ```",
834
+ "5. 查阅微信小程序官方文档(平台差异时):",
835
+ " - https://developers.weixin.qq.com/miniprogram/dev/framework/",
836
+ "6. 整理 2-3 条最相关的解决方案",
837
+ "",
838
+ "**产出格式**:",
839
+ "```",
840
+ "search_keywords: ['taro getLocation fail', 'mini program auth scope'],",
841
+ "docs_hits: [{ title: '...', url: '...', summary: '...' }],",
842
+ "github_issues: [{ number: N, title: '...', url: '...' }],",
843
+ "recommended_solutions: ['...'],",
844
+ "```",
845
+ ].join("\n"),
846
+ suggestedTools: ["Bash", "Read"],
847
+ output: "Taro 文档中相关解决方案",
848
+ },
849
+ { id: "generate-fix", stepTemplate: "generate-fix", depends: ["check-api-permission", "check-package-size", "check-lifecycle", "search-docs"] },
850
+ {
851
+ id: "verify-fix",
852
+ title: "验证修复效果",
853
+ instruction: [
854
+ "## 重新编译并在小程序模拟器验证",
855
+ "",
856
+ "验证修复方案是否真正解决问题,且不引入新问题。",
857
+ "",
858
+ "**执行动作**:",
859
+ "1. 重新编译到微信小程序:",
860
+ " ```bash",
861
+ " npm run dev:weapp 2>&1 | head -30",
862
+ " # 或 npm run build:weapp",
863
+ " ```",
864
+ "2. 确认编译无错误、无警告:",
865
+ " ```bash",
866
+ " npm run build:weapp 2>&1 | grep -E 'error|warning|Error|Warning' | head -20",
867
+ " ```",
868
+ "3. 打开微信开发者工具,加载构建产物:",
869
+ " - 项目目录:`dist/` 或指定目录",
870
+ " - AppID:测试或开发用 AppID",
871
+ "4. 复现之前的问题路径,验证错误是否消失",
872
+ "5. 检查控制台无新增错误或警告:",
873
+ " - 红色错误应为 0",
874
+ " - 黄色警告应不增加",
875
+ "6. 真机调试(如问题与设备相关):",
876
+ " ```bash",
877
+ " # 使用 Taro 真机调试:npm run dev:weapp 后在开发者工具点「真机调试」",
878
+ " ```",
879
+ "7. 跨平台回归验证:",
880
+ " ```bash",
881
+ " npm run build:h5 2>&1 | tail -5",
882
+ " # 必要时再跑 npm run build:alipay",
883
+ " ```",
884
+ "8. 记录验证结果和剩余问题",
885
+ "",
886
+ "**产出格式**:",
887
+ "```",
888
+ "build_result: { weapp: 'pass' | 'fail', h5: 'pass' | 'fail' },",
889
+ "original_issue_fixed: true | false,",
890
+ "new_issues: [{ description: '...' }],",
891
+ "console_errors_count: N,",
892
+ "real_device_tested: true | false,",
893
+ "remaining_issues: ['...'],",
894
+ "```",
895
+ ].join("\n"),
896
+ suggestedTools: ["Bash", "Read"],
897
+ output: "修复验证结果",
898
+ depends: ["generate-fix"],
899
+ },
900
+ ],
901
+ triggers: [{ type: "cli", command: "debug:miniapp" }],
902
+ },
903
+ // ── 性能优化 ──
904
+ {
905
+ name: "performance-optimize",
906
+ description: "小程序性能优化:包体积 → 渲染优化 → 加载速度",
907
+ version: "2.0.0",
908
+ steps: [
909
+ {
910
+ id: "audit-bundle",
911
+ title: "审计包体积",
912
+ instruction: [
913
+ "## 审计包体积(主包大小、分包配置、无用依赖)",
914
+ "",
915
+ "小程序主包限制 2MB,总包 20MB,需要详细分析体积分布。",
916
+ "",
917
+ "**执行动作**:",
918
+ "1. 查看构建产物总体积:",
919
+ " ```bash",
920
+ " du -sh dist/ 2>/dev/null",
921
+ " ```",
922
+ "2. 分析主包各部分体积:",
923
+ " ```bash",
924
+ " du -sh dist/app.* dist/common.* dist/runtime.* 2>/dev/null",
925
+ " find dist/ -maxdepth 2 -type f -exec du -sh {} \\; 2>/dev/null | sort -rh | head -20",
926
+ " ```",
927
+ "3. 分析分包体积:",
928
+ " ```bash",
929
+ " for dir in dist/package*/; do echo \"$dir: $(du -sh $dir 2>/dev/null)\"; done",
930
+ " ```",
931
+ "4. 检查依赖包体积(找出大依赖):",
932
+ " ```bash",
933
+ " npx source-map-explorer 'dist/**/*.js' 2>/dev/null | head -30",
934
+ " # 或使用 analyze 工具",
935
+ " du -sh node_modules/* 2>/dev/null | sort -rh | head -15",
936
+ " ```",
937
+ "5. 检查主包是否超 2MB 警戒线(1.8MB 视为警戒):",
938
+ "6. 检查静态资源(图片、字体)是否打包进主包:",
939
+ " ```bash",
940
+ " find dist/ -type f \\( -name '*.png' -o -name '*.jpg' -o -name '*.svg' -o -name '*.ttf' -o -name '*.woff' \\) -exec ls -lh {} \\;",
941
+ " ```",
942
+ "7. 检查是否有未使用的依赖:",
943
+ " ```bash",
944
+ " grep -rh \"from '[^']*'\" src/ --include='*.ts' --include='*.tsx' | awk -F\"'\" '{print $2}' | sort -u > /tmp/used_deps",
945
+ " grep -E '\"dependencies\"' -A 100 package.json | grep -E '\"[^\"]+\":' | awk -F'\"' '{print $2}' | sort > /tmp/all_deps",
946
+ " comm -23 /tmp/all_deps /tmp/used_deps",
947
+ " ```",
948
+ "",
949
+ "**产出格式**:",
950
+ "```",
951
+ "main_package_size: '1.85MB',",
952
+ "main_package_warning: true | false,",
953
+ "total_size: '8MB',",
954
+ "subpackage_sizes: [{ name: 'packageA', size: '1.2MB' }],",
955
+ "largest_files: [{ path: 'dist/...', size: '500KB' }],",
956
+ "unused_deps: ['moment', 'lodash-es'],",
957
+ "static_assets_in_package: ['dist/images/bg.png (450KB)'],",
958
+ "```",
959
+ ].join("\n"),
960
+ suggestedTools: ["Bash", "Read"],
961
+ output: "包体积审计报告",
962
+ },
963
+ {
964
+ id: "check-lazy-load",
965
+ title: "检查懒加载配置",
966
+ instruction: [
967
+ "## 检查懒加载(组件异步加载 + 图片懒加载)",
968
+ "",
969
+ "小程序首屏加载速度依赖懒加载策略。",
970
+ "",
971
+ "**执行动作**:",
972
+ "1. 检查 Taro 配置中是否开启组件异步加载:",
973
+ " ```bash",
974
+ " grep -rn 'lazyLoad\\|lazyCodeLoad\\|lazyComponent' config/ --include='*.ts' --include='*.js'",
975
+ " cat config/index.ts 2>/dev/null | grep -A 5 -i lazy",
976
+ " ```",
977
+ "2. 检查图片是否使用懒加载:",
978
+ " ```bash",
979
+ " grep -rn '<Image' src/ --include='*.tsx' --include='*.jsx' | grep -v 'lazy-load'",
980
+ " ```",
981
+ " Taro 的 `<Image lazyLoad />` 可启用图片懒加载",
982
+ "3. 检查 `<Image>` 是否使用了合适的 `mode`:",
983
+ " ```bash",
984
+ " grep -rn '<Image' src/ --include='*.tsx' --include='*.jsx' | awk '{print $0}'",
985
+ " ```",
986
+ " - `mode='aspectFill'`、`mode='widthFix'` 等按场景选择",
987
+ "4. 检查是否使用 `Taro.preload` 预加载资源:",
988
+ " ```bash",
989
+ " grep -rn 'Taro.preload\\|preloadRule' src/ --include='*.ts' --include='*.tsx'",
990
+ " ```",
991
+ "5. 检查分包预下载配置:",
992
+ " ```bash",
993
+ " grep -A 10 'preloadRule' src/app.config.ts",
994
+ " ```",
995
+ "6. 检查路由跳转是否使用预加载:",
996
+ " ```bash",
997
+ " grep -rn 'navigateTo\\|switchTab' src/ --include='*.ts' --include='*.tsx' | head -20",
998
+ " ```",
999
+ "",
1000
+ "**产出格式**:",
1001
+ "```",
1002
+ "lazy_load_config: {",
1003
+ " component_lazy: true | false,",
1004
+ " image_lazy_load_used: N,",
1005
+ " image_without_lazy_load: ['src/pages/home/index.tsx:23'],",
1006
+ " preload_rule: true | false,",
1007
+ " taro_preload_used: true | false,",
1008
+ "}",
1009
+ "```",
1010
+ ].join("\n"),
1011
+ suggestedTools: ["Bash", "Read"],
1012
+ output: "懒加载配置检查结果",
1013
+ },
1014
+ {
1015
+ id: "check-setdata",
1016
+ title: "检查 setData 性能",
1017
+ instruction: [
1018
+ "## 检查 setData 调用频率和数据量",
1019
+ "",
1020
+ "小程序 setData(Taro 中对应 setState,底层会转 setData)是性能瓶颈的关键点。",
1021
+ "",
1022
+ "**执行动作**:",
1023
+ "1. 扫描 `setState` 调用:",
1024
+ " ```bash",
1025
+ " grep -rn 'setState\\|useState' src/ --include='*.tsx' --include='*.ts' | grep -v 'test' | head -30",
1026
+ " ```",
1027
+ "2. 检查是否在高频事件中频繁 setState:",
1028
+ " ```bash",
1029
+ " grep -rn 'onScroll\\|onTouchMove\\|onInput\\|onAnimation' src/ --include='*.tsx' --include='*.ts' -A 5 | grep -E 'setState|useState'",
1030
+ " ```",
1031
+ "3. 检查是否在 `useEffect` 中触发大量 setState:",
1032
+ " ```bash",
1033
+ " grep -rn 'useEffect' src/ --include='*.tsx' --include='*.ts' -A 8 | grep -E 'setState'",
1034
+ " ```",
1035
+ "4. 检查 data 中是否包含大对象(应拆分成细粒度):",
1036
+ " ```bash",
1037
+ " grep -rn 'useState(\\[{\\|useState({.*})' src/ --include='*.tsx' --include='*.ts'",
1038
+ " ```",
1039
+ "5. 检查是否有列表级 setState(整个列表替换):",
1040
+ " - 应使用局部更新或路径更新",
1041
+ "6. 检查是否使用了 Taro 的 `useUpdate` 或 `useEffect` 触发重渲染:",
1042
+ " ```bash",
1043
+ " grep -rn 'useUpdate\\|forceUpdate' src/ --include='*.tsx' --include='*.ts'",
1044
+ " ```",
1045
+ "7. 评估 setState 性能影响:",
1046
+ " - 单次 setData 数据量 > 256KB → 警告",
1047
+ " - 1 秒内 setData > 20 次 → 警告",
1048
+ "",
1049
+ "**产出格式**:",
1050
+ "```",
1051
+ "setstate_hot_paths: [{",
1052
+ " file: '...',",
1053
+ " line: N,",
1054
+ " issue: 'setState in onScroll handler',",
1055
+ " severity: 'high' | 'medium' | 'low'",
1056
+ "}],",
1057
+ "large_data_objects: [{ file, line, size_estimate: 'large' }],",
1058
+ "list_level_updates: ['src/pages/list/index.tsx:45'],",
1059
+ "```",
1060
+ ].join("\n"),
1061
+ suggestedTools: ["Bash", "Read"],
1062
+ output: "setData 性能分析结果",
1063
+ },
1064
+ {
1065
+ id: "check-list-rendering",
1066
+ title: "检查长列表渲染",
1067
+ instruction: [
1068
+ "## 检查长列表是否使用 VirtualList",
1069
+ "",
1070
+ "小程序中渲染超长列表会卡死,必须使用虚拟列表。",
1071
+ "",
1072
+ "**执行动作**:",
1073
+ "1. 扫描所有列表/循环渲染:",
1074
+ " ```bash",
1075
+ " grep -rn '\\.map(' src/ --include='*.tsx' --include='*.jsx' | head -30",
1076
+ " ```",
1077
+ "2. 检查是否使用 VirtualList:",
1078
+ " ```bash",
1079
+ " grep -rn 'VirtualList\\|virtualList\\|virtual-list' src/ --include='*.tsx' --include='*.ts'",
1080
+ " ```",
1081
+ "3. 检查长列表的数据源长度:",
1082
+ " - 如果列表项 > 50 个且未使用 VirtualList → 警告",
1083
+ " - 检查是否有分页加载(onReachBottom)",
1084
+ " ```bash",
1085
+ " grep -rn 'useReachBottom\\|onReachBottom\\|pageNo\\|pageSize\\|loadMore' src/ --include='*.tsx' --include='*.ts'",
1086
+ " ```",
1087
+ "4. 检查 `<ScrollView>` 是否用于长列表(不推荐):",
1088
+ " ```bash",
1089
+ " grep -rn '<ScrollView' src/ --include='*.tsx' --include='*.jsx' | grep -v 'VirtualList'",
1090
+ " ```",
1091
+ "5. 检查 `key` 属性是否正确使用:",
1092
+ " ```bash",
1093
+ " grep -rn '\\.map(' src/ --include='*.tsx' -A 3 | grep -E 'key='",
1094
+ " ```",
1095
+ " - 必须使用稳定唯一 key,不要用 index",
1096
+ "6. 检查是否启用了 `@tarojs/plugin-html` 的长列表优化",
1097
+ "",
1098
+ "**产出格式**:",
1099
+ "```",
1100
+ "long_lists: [{",
1101
+ " file: '...',",
1102
+ " line: N,",
1103
+ " data_source: 'items (from useState)',",
1104
+ " estimated_length: '100+',",
1105
+ " uses_virtual_list: true | false,",
1106
+ " uses_pagination: true | false,",
1107
+ " has_stable_key: true | false,",
1108
+ " recommendation: 'VirtualList' | '分页加载' | 'ok'",
1109
+ "}],",
1110
+ "```",
1111
+ ].join("\n"),
1112
+ suggestedTools: ["Bash", "Read"],
1113
+ output: "长列表渲染分析",
1114
+ },
1115
+ {
1116
+ id: "generate-optimizations",
1117
+ title: "生成优化方案",
1118
+ instruction: [
1119
+ "## 生成优化方案(分包、懒加载、代码精简)",
1120
+ "",
1121
+ "基于前面步骤的分析结果,生成综合性能优化方案。",
1122
+ "",
1123
+ "**执行动作**:",
1124
+ "1. 综合 `audit-bundle` 包体积分析,给出分包和瘦身方案:",
1125
+ " - 主包 > 1.8MB → 必须分包",
1126
+ " - 静态资源 → 迁移 CDN",
1127
+ " - 大依赖 → 替换或按需引入",
1128
+ " - 未使用依赖 → 删除",
1129
+ "2. 综合 `check-lazy-load` 分析,给出懒加载方案:",
1130
+ " - 添加 `<Image lazyLoad />`",
1131
+ " - 配置 `preloadRule` 分包预下载",
1132
+ " - 使用 `Taro.preload` 在跳转前预加载",
1133
+ "3. 综合 `check-setdata` 分析,给出 setData 优化方案:",
1134
+ " - 高频事件(scroll、touchMove)加节流",
1135
+ " - 大对象拆分,避免整体替换",
1136
+ " - 使用路径更新替代整体 setState",
1137
+ " - 使用 `React.memo` / `useMemo` 减少重渲染",
1138
+ "4. 综合 `check-list-rendering` 分析,给出列表优化方案:",
1139
+ " - 长列表使用 `@tarojs/components-advanced` 的 VirtualList",
1140
+ " - 添加分页加载(onReachBottom + 增量请求)",
1141
+ " - 使用稳定 key",
1142
+ "5. 按优先级排序优化项(高 ROI 优先)",
1143
+ "6. 每条方案给出预期收益(主包减少 X KB、首屏减少 X ms)",
1144
+ "",
1145
+ "**产出格式**:",
1146
+ "```",
1147
+ "optimizations: [{",
1148
+ " id: 1,",
1149
+ " category: 'bundle-size' | 'lazy-load' | 'setdata' | 'list-rendering',",
1150
+ " title: '...',",
1151
+ " files_to_modify: ['...'],",
1152
+ " effort: 'low' | 'medium' | 'high',",
1153
+ " impact: 'main-package -300KB' | 'first-paint -200ms',",
1154
+ " implementation_steps: ['...'],",
1155
+ "}],",
1156
+ "```",
1157
+ ].join("\n"),
1158
+ suggestedTools: ["Read", "Bash"],
1159
+ output: "综合性能优化方案",
1160
+ depends: ["audit-bundle", "check-lazy-load", "check-setdata", "check-list-rendering"],
1161
+ },
1162
+ {
1163
+ id: "apply-optimizations",
1164
+ title: "应用性能优化",
1165
+ instruction: [
1166
+ "## 应用性能优化",
1167
+ "",
1168
+ "按 `generate-optimizations` 的方案逐项应用优化。",
1169
+ "",
1170
+ "**执行动作**:",
1171
+ "1. **分包优化**:",
1172
+ " - 在 `src/app.config.ts` 中配置 `subPackages`",
1173
+ " - 将非首屏、非 TabBar 页面移到分包",
1174
+ " - 配置 `preloadRule` 分包预下载",
1175
+ "2. **静态资源 CDN 化**:",
1176
+ " - 把 `dist/images/*` 上传到 CDN",
1177
+ " - 代码中引用 CDN URL 而非本地路径",
1178
+ "3. **懒加载配置**:",
1179
+ " - 给所有 `<Image>` 添加 `lazyLoad` 属性",
1180
+ " - 在 `config/index.ts` 中启用 `lazyCodeLoad: true`",
1181
+ "4. **setData 优化**:",
1182
+ " - 给高频事件(scroll、touchMove)加节流:",
1183
+ " ```typescript",
1184
+ " const throttled = useMemo(() => throttle(handler, 100), []);",
1185
+ " ```",
1186
+ " - 把大对象拆成细粒度 state",
1187
+ " - 使用路径更新:",
1188
+ " ```typescript",
1189
+ " setList(prev => [...prev.slice(0, index), newItem, ...prev.slice(index + 1)]);",
1190
+ " ```",
1191
+ "5. **长列表虚拟化**:",
1192
+ " ```typescript",
1193
+ " import { VirtualList } from '@tarojs/components-advanced';",
1194
+ "",
1195
+ " <VirtualList",
1196
+ " height={500}",
1197
+ " itemData={items}",
1198
+ " itemCount={items.length}",
1199
+ " itemSize={80}",
1200
+ " >",
1201
+ " {Row}",
1202
+ " </VirtualList>",
1203
+ " ```",
1204
+ "6. **按需引入依赖**:",
1205
+ " ```typescript",
1206
+ " // 替换 import _ from 'lodash'",
1207
+ " import throttle from 'lodash/throttle';",
1208
+ " ```",
1209
+ "7. 应用每个优化后,记录实际效果",
1210
+ "",
1211
+ "**产出格式**:",
1212
+ "```",
1213
+ "optimizations_applied: [{",
1214
+ " id: 1,",
1215
+ " title: '...',",
1216
+ " files_modified: ['...'],",
1217
+ " result: 'main-package reduced by 300KB',",
1218
+ "}]",
1219
+ "```",
1220
+ ].join("\n"),
1221
+ suggestedTools: ["Read", "Edit", "Write", "Bash"],
1222
+ output: "已应用的性能优化清单",
1223
+ depends: ["generate-optimizations"],
1224
+ },
1225
+ {
1226
+ id: "measure-improvement",
1227
+ title: "测量优化效果",
1228
+ instruction: [
1229
+ "## 测量优化前后首屏时间和包体积",
1230
+ "",
1231
+ "对比优化前后的指标,验证优化效果。",
1232
+ "",
1233
+ "**执行动作**:",
1234
+ "1. 重新构建到微信小程序:",
1235
+ " ```bash",
1236
+ " npm run build:weapp 2>&1 | tail -10",
1237
+ " ```",
1238
+ "2. 测量构建后体积:",
1239
+ " ```bash",
1240
+ " du -sh dist/ 2>/dev/null",
1241
+ " find dist/ -maxdepth 2 -type f -exec du -sh {} \\; 2>/dev/null | sort -rh | head -10",
1242
+ " # 主包体积",
1243
+ " du -sh dist/app.* dist/pages/ 2>/dev/null",
1244
+ " ```",
1245
+ "3. 测量分包体积:",
1246
+ " ```bash",
1247
+ " for dir in dist/package*/; do echo \"$dir: $(du -sh $dir 2>/dev/null)\"; done",
1248
+ " ```",
1249
+ "4. 在微信开发者工具查看性能面板:",
1250
+ " - 「调试器」→「Performance」记录启动过程",
1251
+ " - 关注:脚本执行时间、首次渲染时间、setData 次数和耗时",
1252
+ "5. 真机性能测试(如可用):",
1253
+ " - 「调试器」→「Performance」→真机模式",
1254
+ " - 记录首次渲染时间(FP)和可交互时间(TTI)",
1255
+ "6. 对比优化前后指标:",
1256
+ " - 主包体积:X MB → Y MB(减少 Z%)",
1257
+ " - 首屏时间:X ms → Y ms",
1258
+ " - setData 次数:X/s → Y/s",
1259
+ " - 总包大小:X MB → Y MB",
1260
+ "7. 记录未达标或需要继续优化的项",
1261
+ "",
1262
+ "**产出格式**:",
1263
+ "```",
1264
+ "metrics_comparison: {",
1265
+ " main_package: { before: '1.95MB', after: '1.5MB', reduction: '23%' },",
1266
+ " total_package: { before: '8MB', after: '6MB', reduction: '25%' },",
1267
+ " first_paint: { before: '1200ms', after: '800ms', improvement: '33%' },",
1268
+ " setdata_per_second: { before: 25, after: 8 },",
1269
+ "},",
1270
+ "targets_met: true | false,",
1271
+ "remaining_issues: ['...'],",
1272
+ "further_optimization_suggestions: ['...'],",
1273
+ "```",
1274
+ ].join("\n"),
1275
+ suggestedTools: ["Bash", "Read"],
1276
+ output: "优化前后对比指标",
1277
+ depends: ["apply-optimizations"],
1278
+ },
1279
+ ],
1280
+ triggers: [{ type: "cli", command: "optimize:performance" }],
1281
+ },
1282
+ ],
1283
+ },
1284
+ prompt: {
1285
+ system: "你是一个 Taro 跨端开发专家。使用 Taro 内置组件和 API,确保多端兼容。遇到小程序问题先查文档,不凭经验猜测。",
1286
+ rules: [
1287
+ "使用 Taro 内置组件(View、Text、Image)替代 HTML 元素",
1288
+ "使用 Taro API 替代浏览器 API",
1289
+ "避免使用 window/document 等 DOM API",
1290
+ "使用条件编译处理平台差异(process.env.TARO_ENV)",
1291
+ "样式使用 px 单位(Taro 自动转换)",
1292
+ "合理使用分包加载减少首屏体积",
1293
+ "不确定跨端兼容性时先查 Taro 文档确认",
1294
+ ],
1295
+ },
1296
+ tools: [
1297
+ {
1298
+ name: 'taro_new_page',
1299
+ description: '新建 Taro 页面(跨端兼容 + 路由注册 + 分包配置)',
1300
+ inputSchema: {
1301
+ type: 'object',
1302
+ properties: {
1303
+ pageName: { type: 'string', description: '页面名称' },
1304
+ route: { type: 'string', description: '路由路径' },
1305
+ },
1306
+ required: ['pageName'],
1307
+ },
1308
+ outputSchema: structuredOutputSchema,
1309
+ annotations: readOnlyAnnotations,
1310
+ handler: async (input) => {
1311
+ const { generatePage } = await import('./analyzers/new-page.js');
1312
+ return generatePage(input);
1313
+ },
1314
+ },
1315
+ ],
1316
+ };
1317
+ //# sourceMappingURL=index.js.map