tsheep-skills 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,635 @@
1
+ ---
2
+ name: ab-experiment-cleanup
3
+ description: 清理已推全的 A/B 实验代码,生成影响面分析和回测报告
4
+ ---
5
+
6
+ # A/B 实验代码清理
7
+
8
+ 当 A/B 实验推全后,帮助用户安全地清理实验代码,并生成详细的影响面分析和回测报告。
9
+
10
+ ## 使用场景
11
+
12
+ 当用户说类似以下内容时使用此 skill:
13
+ - "xxx 实验字段对应的实验已经推全,请下掉相关代码"
14
+ - "xxx 字段全量为 true,请删除 false 分支的代码"
15
+ - "实验 xxx 已经结束,需要清理代码并产出影响面分析"
16
+
17
+ ## 工作流程
18
+
19
+ ### 第 1 步:理解实验信息
20
+
21
+ 从用户输入中提取关键信息:
22
+ - **实验字段名称**:例如 `enableNewCheckout`
23
+ - **全量值**:实验推全后的值(通常是 `true` 或 `false`)
24
+ - **要删除的分支**:与全量值相反的分支代码
25
+
26
+ 向用户确认:
27
+ - 实验字段的准确名称
28
+ - 全量值是什么
29
+ - 是否需要删除配置文件中的实验定义
30
+
31
+ ### 第 2 步:代码扫描与分析
32
+
33
+ 使用 Grep 工具搜索实验字段在代码库中的所有出现位置:
34
+
35
+ ```bash
36
+ # 搜索实验字段的所有引用
37
+ grep -r "实验字段名" --include="*.js" --include="*.ts" --include="*.jsx" --include="*.tsx"
38
+ ```
39
+
40
+ 对每个找到的文件:
41
+ 1. 使用 Read 工具读取完整文件内容
42
+ 2. 识别实验相关的代码模式:
43
+ - 条件判断:`if (experimentField)`, `if (!experimentField)`
44
+ - 三元运算符:`experimentField ? A : B`
45
+ - Switch 语句:`switch(experimentField)`
46
+ - 逻辑运算:`experimentField && doSomething()`
47
+ 3. 确定哪些代码块需要保留,哪些需要删除
48
+
49
+ ### 第 3 步:组件迁移兼容性检查
50
+
51
+ **重要:** 如果清理计划涉及组件迁移(从旧版组件迁移到新版组件),必须先进行兼容性检查。
52
+
53
+ #### 3.1 识别组件迁移场景
54
+
55
+ 检查是否存在以下情况:
56
+ - 需要将旧版组件(如 `ComponentName_old`)迁移到新版组件(如 `ComponentName`)
57
+ - 需要更改组件的导入路径
58
+ - 存在外部依赖使用了旧版组件
59
+
60
+ #### 3.2 执行组件兼容性检查
61
+
62
+ 对于每个需要迁移的组件,执行以下检查:
63
+
64
+ **步骤 1:对比核心组件实现**
65
+
66
+ 使用 `diff` 命令对比新旧组件的实现:
67
+
68
+ ```bash
69
+ # 对比主组件
70
+ diff -u src/components/ComponentName_old/index.tsx src/components/ComponentName/index.tsx
71
+
72
+ # 对比子组件
73
+ diff -u src/components/ComponentName_old/SubComponent.tsx src/components/ComponentName/SubComponent.tsx
74
+
75
+ # 对比样式文件
76
+ diff -u src/components/ComponentName_old/index.module.less src/components/ComponentName/index.module.less
77
+ ```
78
+
79
+ **步骤 2:分析差异**
80
+
81
+ 对于发现的差异,分析以下方面:
82
+
83
+ 1. **API 兼容性**
84
+ - Props 是否一致?
85
+ - 新增的 props 是否为可选参数?
86
+ - 是否有 props 被删除或重命名?
87
+
88
+ 2. **类型定义**
89
+ - TypeScript 类型是否兼容?
90
+ - 是否使用了 `any` 类型(更宽松)?
91
+
92
+ 3. **子组件依赖**
93
+ - 子组件是否有变化?
94
+ - 子组件的 API 是否兼容?
95
+
96
+ 4. **功能差异**
97
+ - 新版是否有新增功能?
98
+ - 新增功能是否通过可选参数控制?
99
+ - 是否有功能被移除?
100
+
101
+ 5. **样式差异**
102
+ - 样式是否有变化?
103
+ - 变化是否会影响视觉效果?
104
+
105
+ **步骤 3:生成兼容性报告**
106
+
107
+ 为每个组件生成兼容性分析:
108
+
109
+ ```markdown
110
+ ## 组件兼容性分析:ComponentName
111
+
112
+ ### 核心组件对比
113
+ - **新版**:src/components/ComponentName/index.tsx
114
+ - **旧版**:src/components/ComponentName_old/index.tsx
115
+ - **对比结果**:✅ 完全一致 / ⚠️ 有差异但兼容 / ❌ 不兼容
116
+
117
+ ### 差异说明
118
+ | 差异项 | 旧版 | 新版 | 影响评估 |
119
+ |-------|------|------|---------|
120
+ | 新增 Props | 无 | `newProp1`, `newProp2` | ✅ 无影响(可选参数) |
121
+ | 功能变化 | 旧实现 | 新实现 | ⚠️ 需要测试 |
122
+
123
+ ### 使用方式检查
124
+ - 当前使用的 props:`prop1`, `prop2`, `prop3`
125
+ - 新版是否支持:✅ 全部支持
126
+ - 是否需要修改调用代码:❌ 不需要
127
+
128
+ ### 兼容性结论
129
+ ✅ **安全** - 可以直接迁移
130
+ ⚠️ **需要测试** - 迁移后需要验证功能
131
+ ❌ **不兼容** - 需要修改调用代码
132
+ ```
133
+
134
+ **步骤 4:确认迁移策略**
135
+
136
+ 根据兼容性分析结果,确定迁移策略:
137
+
138
+ - **完全兼容**:直接修改导入路径即可
139
+ - **有差异但兼容**:修改导入路径,并在测试环节重点验证
140
+ - **不兼容**:需要修改调用代码,调整 props 或使用方式
141
+
142
+ #### 3.3 特殊注意事项
143
+
144
+ 1. **子组件的递归检查**
145
+ - 如果组件内部使用了其他子组件,也需要检查子组件的兼容性
146
+ - 使用 `grep` 搜索组件内部的导入语句
147
+
148
+ 2. **样式文件的对比**
149
+ - 即使组件逻辑一致,样式差异也可能导致视觉问题
150
+ - 建议在测试环境验证视觉效果
151
+
152
+ 3. **第三方依赖**
153
+ - 检查新旧组件是否依赖不同版本的第三方库
154
+ - 确保依赖库都已安装
155
+
156
+ ### 第 4 步:生成清理计划
157
+
158
+ 为每个受影响的文件生成详细的清理计划:
159
+
160
+ **文件:** `src/components/Checkout.tsx`
161
+ - **行号 45-67**:删除 `if (!enableNewCheckout)` 分支(旧逻辑)
162
+ - **行号 68-89**:保留 `if (enableNewCheckout)` 分支,移除条件判断
163
+ - **操作**:将第 68-89 行的代码提升到外层,删除条件判断
164
+
165
+ 记录所有需要修改的位置,包括:
166
+ - 业务逻辑代码
167
+ - 配置文件
168
+ - 类型定义
169
+ - 常量定义
170
+ - **组件迁移**(如果有)
171
+
172
+ #### 4.1 最小化变更原则 ⚠️ 重要
173
+
174
+ **核心原则:只修改与实验直接相关的代码,不要动其他逻辑**
175
+
176
+ 这是非常重要的原则,因为:
177
+ 1. **减少回测范围**:只测试实验相关的功能,不需要测试无关的功能
178
+ 2. **降低风险**:避免引入新的 bug
179
+ 3. **提高效率**:减少代码审查和测试的工作量
180
+
181
+ **正确的做法:**
182
+
183
+ ✅ **只删除实验分支的代码**
184
+ ```typescript
185
+ // 修改前
186
+ return experimentFlag ? <NewComponent /> : <OldComponent />;
187
+
188
+ // 修改后
189
+ return <NewComponent />;
190
+ ```
191
+
192
+ ✅ **只删除实验相关的导入**
193
+ ```typescript
194
+ // 修改前
195
+ import OldComponent from './OldComponent';
196
+
197
+ // 修改后
198
+ // 删除这一行
199
+ ```
200
+
201
+ ❌ **不要删除无关的逻辑**
202
+ ```typescript
203
+ // 错误示例:不要删除初始化逻辑
204
+ const experimentFlag = useExperiment();
205
+ const [init, setInit] = useState(false);
206
+
207
+ // 初始化逻辑(与实验无关,不要删除)
208
+ useEffect(() => {
209
+ initSomething();
210
+ }, []);
211
+
212
+ // 只删除这里的条件判断
213
+ return experimentFlag ? <NewComponent /> : <OldComponent />;
214
+ ```
215
+
216
+ ❌ **不要简化整个文件**
217
+ ```typescript
218
+ // 错误示例:不要把整个文件简化成几行
219
+ // 这会删除很多无关的逻辑,增加回测范围
220
+
221
+ // 错误的做法
222
+ export default function Entry() {
223
+ return <NewComponent />;
224
+ }
225
+
226
+ // 正确的做法:保留所有无关的逻辑
227
+ export default function Entry() {
228
+ const experimentFlag = useExperiment();
229
+ const [init, setInit] = useState(false);
230
+
231
+ // 保留初始化逻辑
232
+ useEffect(() => {
233
+ initSomething();
234
+ }, []);
235
+
236
+ // 保留 Loading 状态
237
+ if (!init) {
238
+ return <Loading />;
239
+ }
240
+
241
+ // 只修改这里
242
+ return <NewComponent />;
243
+ }
244
+ ```
245
+
246
+ **实际案例:**
247
+
248
+ 假设有以下代码:
249
+ ```typescript
250
+ export default function Entry() {
251
+ const experimentFlag = useExperiment();
252
+ const [init, setInit] = useState(false);
253
+
254
+ async function initialize() {
255
+ // 初始化逻辑(与实验无关)
256
+ await doSomething();
257
+ setInit(true);
258
+ }
259
+
260
+ useEffect(() => {
261
+ initialize();
262
+ // 埋点(与实验无关)
263
+ trackEvent('page_view');
264
+ }, []);
265
+
266
+ if (!init) {
267
+ return <Loading />;
268
+ }
269
+
270
+ // 实验判断
271
+ return experimentFlag ? <NewVersion /> : <OldVersion />;
272
+ }
273
+ ```
274
+
275
+ **正确的清理方式:**
276
+ ```typescript
277
+ export default function Entry() {
278
+ const experimentFlag = useExperiment(); // 保留(可能其他地方用到)
279
+ const [init, setInit] = useState(false); // 保留
280
+
281
+ async function initialize() {
282
+ // 保留(与实验无关)
283
+ await doSomething();
284
+ setInit(true);
285
+ }
286
+
287
+ useEffect(() => {
288
+ initialize(); // 保留
289
+ trackEvent('page_view'); // 保留
290
+ }, []);
291
+
292
+ if (!init) {
293
+ return <Loading />; // 保留
294
+ }
295
+
296
+ // 只修改这里
297
+ return <NewVersion />;
298
+ }
299
+ ```
300
+
301
+ **需要删除的内容:**
302
+ 1. ✂️ `OldVersion` 组件的导入
303
+ 2. ✂️ 条件判断中的 `<OldVersion />` 分支
304
+
305
+ **需要保留的内容:**
306
+ 1. ✅ 所有初始化逻辑
307
+ 2. ✅ 所有状态管理
308
+ 3. ✅ 所有埋点代码
309
+ 4. ✅ 所有 Loading 状态
310
+ 5. ✅ 所有与实验无关的逻辑
311
+
312
+ #### 4.2 清理范围检查清单
313
+
314
+ 在生成清理计划时,使用以下检查清单确保遵循最小化变更原则:
315
+
316
+ - [ ] 是否只删除了实验分支的代码?
317
+ - [ ] 是否保留了所有初始化逻辑?
318
+ - [ ] 是否保留了所有状态管理?
319
+ - [ ] 是否保留了所有埋点代码?
320
+ - [ ] 是否保留了所有 Loading 状态?
321
+ - [ ] 是否保留了所有错误处理?
322
+ - [ ] 是否只删除了必要的导入?
323
+ - [ ] 变更是否最小化?
324
+
325
+ ### 第 5 步:影响面分析
326
+
327
+ 生成详细的影响面分析报告,包含以下部分:
328
+
329
+ #### 5.1 受影响文件清单
330
+ 列出所有需要修改的文件,按类型分类:
331
+ - **业务逻辑文件**:列出文件路径和修改类型
332
+ - **配置文件**:实验配置、feature flag 配置
333
+ - **类型定义文件**:TypeScript 接口、类型定义
334
+ - **测试文件**:相关的测试用例
335
+
336
+ #### 5.2 功能模块影响
337
+ 分析受影响的功能模块:
338
+ - 识别涉及的业务功能(如:结账流程、用户注册等)
339
+ - 评估每个功能的修改范围(轻微/中等/重大)
340
+ - 标注关键路径和核心功能
341
+
342
+ #### 5.3 依赖关系分析
343
+ - 检查是否有其他代码依赖该实验字段
344
+ - 识别可能的连锁影响
345
+ - 标注需要特别注意的依赖关系
346
+
347
+ #### 5.4 风险评估
348
+ 评估清理操作的风险等级:
349
+ - **低风险**:简单的条件分支删除,无复杂依赖
350
+ - **中风险**:涉及多个文件,有一定依赖关系
351
+ - **高风险**:核心功能修改,复杂的逻辑变更
352
+
353
+ #### 5.5 生成影响面分析文件
354
+ 使用 Write 工具生成 Markdown 格式的影响面分析报告文件:
355
+
356
+ **文件命名规范:**
357
+ ```
358
+ docs/ab-experiment-cleanup/impact-analysis-{实验字段名}-{YYYY-MM-DD}.md
359
+ ```
360
+
361
+ **文件内容包含:**
362
+ - 实验信息摘要
363
+ - 受影响文件清单(表格形式)
364
+ - 功能模块影响分析
365
+ - 依赖关系图
366
+ - 风险评估和建议
367
+
368
+ ### 第 6 步:生成回测报告
369
+
370
+ 生成详细的业务回测报告,包含:
371
+
372
+ #### 6.1 回测检查清单
373
+ 为每个受影响的功能模块生成回测点:
374
+
375
+ **功能模块:结账流程**
376
+ - [ ] 验证新用户结账流程正常
377
+ - [ ] 验证老用户结账流程正常
378
+ - [ ] 验证优惠券应用逻辑
379
+ - [ ] 验证支付成功后的跳转
380
+ - [ ] 验证错误处理逻辑
381
+
382
+ #### 6.2 测试用例建议
383
+ - 列出需要执行的自动化测试
384
+ - 建议新增的测试用例(如果原有测试覆盖不足)
385
+ - 标注需要手动测试的场景
386
+
387
+ #### 6.3 数据验证建议
388
+ - 建议监控的关键指标(如转化率、错误率)
389
+ - 建议的灰度发布策略
390
+ - 回滚预案
391
+
392
+ #### 6.4 生成回测报告文件
393
+ 使用 Write 工具生成 Markdown 格式的回测报告文件:
394
+
395
+ **文件命名规范:**
396
+ ```
397
+ docs/ab-experiment-cleanup/test-plan-{实验字段名}-{YYYY-MM-DD}.md
398
+ ```
399
+
400
+ **文件内容包含:**
401
+ - 回测检查清单(按功能模块分类)
402
+ - 自动化测试建议
403
+ - 手动测试场景
404
+ - 数据验证和监控建议
405
+ - 回滚预案
406
+
407
+ ### 第 7 步:执行代码清理
408
+
409
+ 在用户确认清理计划后,执行代码修改:
410
+
411
+ #### 7.1 最小化变更原则(再次强调)⚠️
412
+
413
+ **在执行清理前,必须再次确认:**
414
+ - ✅ 只修改与实验直接相关的代码
415
+ - ✅ 不删除任何无关的逻辑
416
+ - ✅ 不简化整个文件
417
+ - ✅ 保留所有初始化、状态管理、埋点、Loading 等逻辑
418
+
419
+ **错误示例:**
420
+ ```typescript
421
+ // ❌ 错误:把整个文件简化成几行
422
+ export default function Entry() {
423
+ return <NewComponent />;
424
+ }
425
+ ```
426
+
427
+ **正确示例:**
428
+ ```typescript
429
+ // ✅ 正确:只删除实验分支,保留其他逻辑
430
+ export default function Entry() {
431
+ const experimentFlag = useExperiment();
432
+ const [init, setInit] = useState(false);
433
+
434
+ // 保留初始化逻辑
435
+ useEffect(() => {
436
+ initialize();
437
+ }, []);
438
+
439
+ // 保留 Loading 状态
440
+ if (!init) {
441
+ return <Loading />;
442
+ }
443
+
444
+ // 只修改这里:删除条件判断
445
+ return <NewComponent />;
446
+ }
447
+ ```
448
+
449
+ #### 7.2 逐文件处理
450
+
451
+ 1. **业务逻辑文件**:
452
+ - 使用 Edit 工具进行精确的代码修改
453
+ - **只删除实验分支的代码**
454
+ - **只删除实验相关的导入**
455
+ - **保留所有其他逻辑**
456
+ - 不要"简化"或"优化"无关的代码
457
+
458
+ 2. **清理配置**:
459
+ - 删除实验配置文件中的相关定义
460
+ - 更新类型定义文件
461
+ - 清理不再使用的常量
462
+
463
+ 3. **更新测试**:
464
+ - 删除实验特定的测试用例
465
+ - 更新相关的测试断言
466
+ - 确保测试仍然通过
467
+
468
+ #### 7.3 执行前检查清单
469
+
470
+ 在执行每个文件的修改前,检查:
471
+
472
+ - [ ] 是否只删除了实验分支的代码?
473
+ - [ ] 是否保留了所有初始化逻辑?
474
+ - [ ] 是否保留了所有状态管理?
475
+ - [ ] 是否保留了所有埋点代码?
476
+ - [ ] 是否保留了所有 Loading 状态?
477
+ - [ ] 是否保留了所有错误处理?
478
+ - [ ] 是否只删除了必要的导入?
479
+ - [ ] 变更是否最小化?
480
+
481
+ #### 7.4 常见错误和正确做法
482
+
483
+ | 错误做法 | 正确做法 | 原因 |
484
+ |---------|---------|------|
485
+ | 删除整个初始化逻辑 | 只删除实验分支 | 初始化逻辑可能被其他功能使用 |
486
+ | 删除所有状态管理 | 只删除实验相关的状态 | 状态可能被其他功能使用 |
487
+ | 删除所有埋点代码 | 只删除实验相关的埋点 | 埋点用于监控,不应删除 |
488
+ | 简化整个文件 | 最小化修改 | 增加回测范围和风险 |
489
+ | 删除 Loading 状态 | 保留 Loading 状态 | Loading 状态与实验无关 |
490
+
491
+ ### 第 8 步:生成最终报告
492
+
493
+ 生成一份完整的清理报告,包含:
494
+
495
+ #### 变更摘要
496
+ - 实验名称和字段
497
+ - 修改的文件数量
498
+ - 删除的代码行数
499
+ - 保留的代码行数
500
+
501
+ #### 详细变更列表
502
+ 每个文件的具体变更:
503
+ ```
504
+ src/components/Checkout.tsx
505
+ - 删除第 45-67 行(旧结账逻辑)
506
+ - 简化第 68-89 行(移除条件判断)
507
+
508
+ src/config/experiments.ts
509
+ - 删除 enableNewCheckout 配置
510
+ ```
511
+
512
+ #### 影响面分析(汇总)
513
+ - 受影响的功能模块列表
514
+ - 风险等级评估
515
+ - 关键依赖说明
516
+
517
+ #### 回测建议(汇总)
518
+ - 必须执行的回测项
519
+ - 建议的测试策略
520
+ - 监控指标建议
521
+
522
+ #### 后续行动项
523
+ - [ ] 执行自动化测试
524
+ - [ ] 进行手动回测
525
+ - [ ] 监控关键指标
526
+ - [ ] 准备回滚方案(如需要)
527
+
528
+ #### 8.1 生成最终清理报告文件
529
+ 使用 Write 工具生成 Markdown 格式的完整清理报告文件:
530
+
531
+ **文件命名规范:**
532
+ ```
533
+ docs/ab-experiment-cleanup/cleanup-report-{实验字段名}-{YYYY-MM-DD}.md
534
+ ```
535
+
536
+ **文件内容包含:**
537
+ - 变更摘要
538
+ - 详细变更列表
539
+ - 影响面分析汇总(链接到详细报告)
540
+ - 回测建议汇总(链接到详细报告)
541
+ - 后续行动项清单
542
+
543
+ ## 输出格式
544
+
545
+ ### 控制台输出
546
+ 在执行过程中,向用户展示进度和关键信息:
547
+ - 使用标题和子标题组织内容
548
+ - 使用代码块展示代码变更
549
+ - 使用表格展示文件清单
550
+ - 使用复选框列表展示回测项
551
+ - 使用引用块标注重要提示
552
+
553
+ ### Markdown 文件输出
554
+ **必须生成以下 3 个 Markdown 文件:**
555
+
556
+ #### 1. 影响面分析报告
557
+ - **路径**:`docs/ab-experiment-cleanup/impact-analysis-{实验字段名}-{YYYY-MM-DD}.md`
558
+ - **时机**:第 4 步完成后立即生成
559
+ - **内容**:完整的影响面分析,包括文件清单、功能模块影响、依赖关系、风险评估
560
+
561
+ #### 2. 回测报告
562
+ - **路径**:`docs/ab-experiment-cleanup/test-plan-{实验字段名}-{YYYY-MM-DD}.md`
563
+ - **时机**:第 5 步完成后立即生成
564
+ - **内容**:完整的回测计划,包括检查清单、测试建议、监控指标、回滚预案
565
+
566
+ #### 3. 最终清理报告
567
+ - **路径**:`docs/ab-experiment-cleanup/cleanup-report-{实验字段名}-{YYYY-MM-DD}.md`
568
+ - **时机**:第 7 步完成后生成(如果执行了代码清理)
569
+ - **内容**:变更摘要、详细变更列表、后续行动项
570
+
571
+ **文件命名说明:**
572
+ - `{实验字段名}`:使用实际的实验字段名,如 `enableNewCheckout`
573
+ - `{YYYY-MM-DD}`:使用当前日期,如 `2026-02-02`
574
+ - 示例:`impact-analysis-enableNewCheckout-2026-02-02.md`
575
+
576
+ **目录结构:**
577
+ ```
578
+ docs/
579
+ └── ab-experiment-cleanup/
580
+ ├── impact-analysis-{实验字段名}-{日期}.md
581
+ ├── test-plan-{实验字段名}-{日期}.md
582
+ └── cleanup-report-{实验字段名}-{日期}.md
583
+ ```
584
+
585
+ 如果 `docs/ab-experiment-cleanup/` 目录不存在,需要先创建。
586
+
587
+ ## 注意事项
588
+
589
+ 1. **最小化变更原则**(最重要)⚠️:
590
+ - **只修改与实验直接相关的代码**
591
+ - **不要删除任何无关的逻辑**(初始化、状态管理、埋点、Loading 等)
592
+ - **不要简化整个文件**
593
+ - **目的**:减少回测范围,降低风险,提高效率
594
+ - 这是最重要的原则,必须严格遵守
595
+
596
+ 2. **安全第一**:在执行任何代码修改前,必须先生成清理计划并获得用户确认
597
+
598
+ 3. **完整性**:确保搜索所有可能的文件类型(.js, .ts, .jsx, .tsx, .vue 等)
599
+
600
+ 4. **准确性**:仔细识别条件逻辑,避免误删代码
601
+
602
+ 5. **可读性**:清理后的代码应该保持原有的结构和可读性
603
+
604
+ 6. **测试覆盖**:强调回测的重要性,提供详细的测试建议
605
+
606
+ 7. **文件生成**:
607
+ - 必须使用 Write 工具生成 Markdown 文件,不能只在控制台输出
608
+ - 生成文件前先检查目录是否存在,不存在则创建
609
+ - 文件命名必须遵循规范,包含实验字段名和日期
610
+ - 生成文件后,向用户明确告知文件路径
611
+
612
+ 8. **文档归档**:生成的报告文件应该提交到代码仓库,作为变更记录
613
+
614
+ ## 示例对话
615
+
616
+ **用户:** "enableNewCheckout 实验字段对应的实验已经推全,enableNewCheckout 字段全量为 true,请下掉走到 enableNewCheckout 为 false 的业务逻辑代码,并产出影响面分析和业务回测报告"
617
+
618
+ **Agent 响应:**
619
+ 1. 确认实验信息
620
+ 2. 扫描代码库找到所有 `enableNewCheckout` 的引用
621
+ 3. 分析每个文件的代码逻辑
622
+ 4. **检查组件迁移兼容性**(如果涉及组件迁移)
623
+ - 使用 diff 对比新旧组件
624
+ - 分析 API 兼容性
625
+ - 检查子组件依赖
626
+ - 生成兼容性报告
627
+ 5. 生成清理计划
628
+ 6. 生成影响面分析报告(控制台 + MD 文件)
629
+ - 创建 `docs/ab-experiment-cleanup/impact-analysis-enableNewCheckout-2026-02-02.md`
630
+ 7. 生成回测报告(控制台 + MD 文件)
631
+ - 创建 `docs/ab-experiment-cleanup/test-plan-enableNewCheckout-2026-02-02.md`
632
+ 8. 等待用户确认后执行清理
633
+ 9. 生成最终变更报告(控制台 + MD 文件)
634
+ - 创建 `docs/ab-experiment-cleanup/cleanup-report-enableNewCheckout-2026-02-02.md`
635
+ 10. 告知用户所有生成的文件路径
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { execSync } = require('child_process');
4
+
5
+ console.log('\n🔍 NPM 认证诊断工具\n');
6
+ console.log('='.repeat(50));
7
+
8
+ // 检查是否已登录
9
+ console.log('\n1. 检查登录状态...');
10
+ try {
11
+ const username = execSync('npm whoami', { encoding: 'utf-8' }).trim();
12
+ console.log(`✅ 已登录为: ${username}`);
13
+ } catch (error) {
14
+ console.log('❌ 未登录或无法连接到 npm registry');
15
+ console.log(' 请运行: npm login');
16
+ process.exit(1);
17
+ }
18
+
19
+ // 检查 registry 配置
20
+ console.log('\n2. 检查 registry 配置...');
21
+ try {
22
+ const registry = execSync('npm config get registry', { encoding: 'utf-8' }).trim();
23
+ console.log(`📦 Registry: ${registry}`);
24
+ if (!registry.includes('npmjs.org')) {
25
+ console.log('⚠️ 警告: 当前 registry 不是 npmjs.org');
26
+ }
27
+ } catch (error) {
28
+ console.log('⚠️ 无法获取 registry 配置');
29
+ }
30
+
31
+ // 检查认证 token
32
+ console.log('\n3. 检查认证 token...');
33
+ try {
34
+ const token = execSync('npm config get //registry.npmjs.org/:_authToken', { encoding: 'utf-8' }).trim();
35
+ if (token && token !== 'undefined') {
36
+ console.log('✅ 找到认证 token');
37
+ console.log(` Token 前缀: ${token.substring(0, 10)}...`);
38
+ } else {
39
+ console.log('⚠️ 未找到认证 token');
40
+ }
41
+ } catch (error) {
42
+ console.log('⚠️ 无法检查 token');
43
+ }
44
+
45
+ console.log('\n' + '='.repeat(50));
46
+ console.log('\n📋 诊断结果和建议:\n');
47
+
48
+ console.log('如果你遇到 403 错误 "Two-factor authentication required":');
49
+ console.log('');
50
+ console.log('🔐 解决方案 1: 启用 2FA(推荐)');
51
+ console.log(' 1. 访问 https://www.npmjs.com/');
52
+ console.log(' 2. 登录后,点击右上角头像 → Account Settings');
53
+ console.log(' 3. 找到 "Two-Factor Authentication" 或 "Security"');
54
+ console.log(' 4. 点击 "Enable 2FA"');
55
+ console.log(' 5. 选择认证方式(推荐使用 Authenticator App)');
56
+ console.log(' 6. 完成设置后,重新登录: npm login');
57
+ console.log('');
58
+ console.log('🔑 解决方案 2: 使用 Granular Access Token');
59
+ console.log(' 1. 访问 https://www.npmjs.com/settings/[你的用户名]/tokens');
60
+ console.log(' 2. 点击 "Generate New Token" → "Granular Access Token"');
61
+ console.log(' 3. 权限选择: "Publish packages"');
62
+ console.log(' 4. 生成后复制 token');
63
+ console.log(' 5. 使用 token 登录: npm login --auth-type=legacy');
64
+ console.log('');
65
+ console.log('💡 提示: 这是 npm 的强制安全策略,无法绕过');
66
+ console.log('');