ethan-skill 1.7.0 → 1.9.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.
Files changed (63) hide show
  1. package/README.md +84 -24
  2. package/dist/cli/index.js +3 -2
  3. package/dist/cli/index.js.map +1 -1
  4. package/dist/skills/15-git-workflow.d.ts +3 -0
  5. package/dist/skills/15-git-workflow.d.ts.map +1 -0
  6. package/dist/skills/15-git-workflow.js +288 -0
  7. package/dist/skills/15-git-workflow.js.map +1 -0
  8. package/dist/skills/16-unit-testing.d.ts +3 -0
  9. package/dist/skills/16-unit-testing.d.ts.map +1 -0
  10. package/dist/skills/16-unit-testing.js +298 -0
  11. package/dist/skills/16-unit-testing.js.map +1 -0
  12. package/dist/skills/17-system-design.d.ts +3 -0
  13. package/dist/skills/17-system-design.d.ts.map +1 -0
  14. package/dist/skills/17-system-design.js +294 -0
  15. package/dist/skills/17-system-design.js.map +1 -0
  16. package/dist/skills/18-database-optimize.d.ts +3 -0
  17. package/dist/skills/18-database-optimize.d.ts.map +1 -0
  18. package/dist/skills/18-database-optimize.js +294 -0
  19. package/dist/skills/18-database-optimize.js.map +1 -0
  20. package/dist/skills/19-docker.d.ts +3 -0
  21. package/dist/skills/19-docker.d.ts.map +1 -0
  22. package/dist/skills/19-docker.js +360 -0
  23. package/dist/skills/19-docker.js.map +1 -0
  24. package/dist/skills/20-cicd.d.ts +3 -0
  25. package/dist/skills/20-cicd.d.ts.map +1 -0
  26. package/dist/skills/20-cicd.js +364 -0
  27. package/dist/skills/20-cicd.js.map +1 -0
  28. package/dist/skills/21-performance.d.ts +3 -0
  29. package/dist/skills/21-performance.d.ts.map +1 -0
  30. package/dist/skills/21-performance.js +139 -0
  31. package/dist/skills/21-performance.js.map +1 -0
  32. package/dist/skills/22-refactoring.d.ts +3 -0
  33. package/dist/skills/22-refactoring.d.ts.map +1 -0
  34. package/dist/skills/22-refactoring.js +235 -0
  35. package/dist/skills/22-refactoring.js.map +1 -0
  36. package/dist/skills/23-observability.d.ts +3 -0
  37. package/dist/skills/23-observability.d.ts.map +1 -0
  38. package/dist/skills/23-observability.js +266 -0
  39. package/dist/skills/23-observability.js.map +1 -0
  40. package/dist/skills/24-design-patterns.d.ts +3 -0
  41. package/dist/skills/24-design-patterns.d.ts.map +1 -0
  42. package/dist/skills/24-design-patterns.js +258 -0
  43. package/dist/skills/24-design-patterns.js.map +1 -0
  44. package/dist/skills/index.d.ts +10 -0
  45. package/dist/skills/index.d.ts.map +1 -1
  46. package/dist/skills/index.js +41 -1
  47. package/dist/skills/index.js.map +1 -1
  48. package/dist/skills/skills.test.js +3 -3
  49. package/dist/skills/skills.test.js.map +1 -1
  50. package/dist/templates/templates.test.js +2 -3
  51. package/dist/templates/templates.test.js.map +1 -1
  52. package/package.json +1 -1
  53. package/rules/claude-code/CLAUDE.md +2410 -3
  54. package/rules/cline/.clinerules +2262 -2
  55. package/rules/codebuddy/CODEBUDDY.md +2361 -2
  56. package/rules/continue/.continuerules +2262 -2
  57. package/rules/copilot/copilot-instructions.md +2331 -2
  58. package/rules/cursor/.cursorrules +2399 -2
  59. package/rules/cursor/smart-flow.mdc +2399 -2
  60. package/rules/jetbrains/smart-flow.md +2331 -2
  61. package/rules/lingma/smart-flow.md +2352 -3
  62. package/rules/windsurf/.windsurf/rules/smart-flow.md +2332 -3
  63. package/rules/zed/smart-flow.rules +2251 -1
@@ -0,0 +1,288 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.gitWorkflowSkill = void 0;
4
+ exports.gitWorkflowSkill = {
5
+ id: 'git-workflow',
6
+ name: 'Git 工作流',
7
+ nameEn: 'git_workflow',
8
+ order: 15,
9
+ category: '执行侧',
10
+ description: '规范 Git 分支策略、提交规范、合并流程,建立团队一致的版本控制工作流',
11
+ descriptionEn: 'Establish consistent Git branching strategy, commit conventions, and merge workflow for teams',
12
+ detailDescription: `系统梳理 Git 工作流全流程,涵盖 GitFlow/Trunk-Based 分支策略选型、Conventional Commits 提交规范、
13
+ rebase vs merge 决策、冲突解决流程和 PR/MR 最佳实践,帮助团队建立高效、可追溯的版本控制体系。`,
14
+ triggers: [
15
+ 'Git 工作流',
16
+ 'git workflow',
17
+ 'git 规范',
18
+ '分支策略',
19
+ 'branching strategy',
20
+ 'commit 规范',
21
+ 'commit convention',
22
+ '提交规范',
23
+ 'PR 规范',
24
+ 'rebase vs merge',
25
+ '冲突解决',
26
+ '@ethan git',
27
+ '@ethan git-workflow',
28
+ ],
29
+ steps: [
30
+ {
31
+ title: '1. 评估项目特征,选择分支策略',
32
+ content: `根据团队规模和发布节奏选择合适的分支策略:
33
+
34
+ **GitFlow 适用场景**
35
+ - 有明确版本号的产品(如 App、SDK、开源库)
36
+ - 需要维护多个线上版本
37
+ - 发布周期较长(周/月级别)
38
+
39
+ \`\`\`
40
+ main ──●────────────────────●── (生产稳定)
41
+ hotfix/1.0.1 └──●──┘ (紧急修复)
42
+ release/1.1 └──●──┘ (预发布验证)
43
+ develop ──●──────●──────●──────●── (集成分支)
44
+ feature/login └──●──┘ (功能开发)
45
+ \`\`\`
46
+
47
+ **Trunk-Based Development 适用场景**
48
+ - 持续部署(CD)体系成熟
49
+ - 有完善的 Feature Flag 机制
50
+ - 团队规模适中(≤50 人),发布频率高(日/周)
51
+
52
+ \`\`\`
53
+ main ──●──●──●──●──●── (直接推送或短命分支 <2天)
54
+ feat └──●──┘ (短命功能分支,快速合并)
55
+ \`\`\`
56
+
57
+ **决策矩阵**
58
+
59
+ | 维度 | GitFlow | Trunk-Based |
60
+ |------|---------|-------------|
61
+ | 发布频率 | 低(周/月) | 高(日/周) |
62
+ | 团队规模 | 大 | 中小 |
63
+ | 多版本维护 | 支持 | 不擅长 |
64
+ | CI/CD 成熟度 | 低要求 | 高要求 |`,
65
+ },
66
+ {
67
+ title: '2. 制定提交信息规范(Conventional Commits)',
68
+ content: `采用 Conventional Commits 规范,格式:\`<type>(<scope>): <subject>\`
69
+
70
+ **类型(type)定义**
71
+
72
+ | type | 用途 | 版本影响 |
73
+ |------|------|---------|
74
+ | \`feat\` | 新功能 | MINOR |
75
+ | \`fix\` | Bug 修复 | PATCH |
76
+ | \`perf\` | 性能优化 | PATCH |
77
+ | \`refactor\` | 重构(无功能变化) | — |
78
+ | \`docs\` | 文档变更 | — |
79
+ | \`test\` | 测试相关 | — |
80
+ | \`chore\` | 构建/依赖/工具 | — |
81
+ | \`ci\` | CI 配置变更 | — |
82
+ | \`BREAKING CHANGE\` | 破坏性变更(Footer) | MAJOR |
83
+
84
+ **示例**
85
+ \`\`\`bash
86
+ # 好的提交信息
87
+ feat(auth): add OAuth2 login with Google provider
88
+ fix(cart): prevent duplicate item addition on rapid click
89
+ perf(query): add composite index on (user_id, created_at)
90
+ refactor(api): extract pagination helper to shared utils
91
+ docs(readme): update installation steps for Node 20
92
+
93
+ # 破坏性变更写法
94
+ feat(api)!: rename /users endpoint to /accounts
95
+
96
+ BREAKING CHANGE: /users endpoint removed, use /accounts instead
97
+ \`\`\`
98
+
99
+ **工具链配置**
100
+ \`\`\`bash
101
+ # 安装 commitlint
102
+ npm install -D @commitlint/cli @commitlint/config-conventional
103
+ echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js
104
+
105
+ # 配合 husky 在 commit-msg 钩子校验
106
+ npx husky add .husky/commit-msg 'npx --no -- commitlint --edit $1'
107
+ \`\`\``,
108
+ },
109
+ {
110
+ title: '3. Rebase vs Merge 决策与实践',
111
+ content: `**核心原则:黄金法则 — 不要 rebase 已推送的公共分支**
112
+
113
+ **何时用 Merge**
114
+ - 合并长期分支(feature → develop)
115
+ - 需要保留完整历史记录(审计场景)
116
+ - 多人协作的共享分支
117
+
118
+ \`\`\`bash
119
+ # 保留合并记录(推荐用于 PR/MR 合并)
120
+ git merge --no-ff feature/login
121
+
122
+ # 快进合并(适合独立小修改)
123
+ git merge --ff-only hotfix/typo
124
+ \`\`\`
125
+
126
+ **何时用 Rebase**
127
+ - 更新本地功能分支,与主干保持同步
128
+ - 整理本地提交历史,推送 PR 前清理
129
+
130
+ \`\`\`bash
131
+ # 将功能分支变基到最新 main
132
+ git checkout feature/login
133
+ git rebase origin/main
134
+
135
+ # 交互式 rebase:合并/重排/修改最近 3 个提交
136
+ git rebase -i HEAD~3
137
+ # 选项: pick / squash(s) / fixup(f) / reword(r) / drop(d)
138
+ \`\`\`
139
+
140
+ **Squash Merge**(GitHub/GitLab PR 推荐)
141
+ \`\`\`bash
142
+ # 将功能分支所有提交合并为一个干净提交
143
+ git merge --squash feature/login
144
+ git commit -m "feat(auth): add login page with form validation"
145
+ \`\`\`
146
+
147
+ **推荐工作流**
148
+ 1. 本地开发:随意提交,保持节奏
149
+ 2. 推送 PR 前:\`git rebase -i origin/main\` 整理提交
150
+ 3. PR 合并:使用 Squash Merge 保持主干干净`,
151
+ },
152
+ {
153
+ title: '4. 冲突解决流程',
154
+ content: `**结构化冲突解决步骤**
155
+
156
+ \`\`\`bash
157
+ # Step 1: 理解冲突来源
158
+ git log --oneline --graph --all # 查看分支关系
159
+ git diff HEAD origin/main # 对比差异
160
+
161
+ # Step 2: 标记冲突文件分析
162
+ git status # 查看所有冲突文件
163
+ # conflict markers: <<<<<<< HEAD ... ======= ... >>>>>>> branch
164
+
165
+ # Step 3: 使用工具辅助解决
166
+ git mergetool # 调用配置的 merge tool(VSCode / IntelliJ)
167
+
168
+ # 配置 VSCode 为默认 merge tool
169
+ git config --global merge.tool vscode
170
+ git config --global mergetool.vscode.cmd 'code --wait $MERGED'
171
+ \`\`\`
172
+
173
+ **三路合并理解(Three-way merge)**
174
+ \`\`\`
175
+ BASE(公共祖先):const timeout = 5000;
176
+ OURS(当前分支):const timeout = 10000; // 改为10s
177
+ THEIRS(被合并):const TIMEOUT = 5000; // 改为大写常量名
178
+ RESULT(手动): const TIMEOUT = 10000; // 两个改动都要
179
+ \`\`\`
180
+
181
+ **预防冲突的最佳实践**
182
+ - 功能分支生命周期控制在 1-3 天内
183
+ - 每日同步主干:\`git pull --rebase origin main\`
184
+ - 大文件/自动生成文件加入 \`.gitattributes\` 配置合并策略
185
+ \`\`\`gitattributes
186
+ # 始终使用 ours 策略合并 lock 文件(减少冲突)
187
+ package-lock.json merge=ours
188
+ yarn.lock merge=ours
189
+ \`\`\``,
190
+ },
191
+ {
192
+ title: '5. Pull Request / Code Review 流程规范',
193
+ content: `**PR 模板设计**
194
+ \`\`\`markdown
195
+ ## 变更说明
196
+ [简洁描述本次变更做了什么、为什么]
197
+
198
+ ## 变更类型
199
+ - [ ] 新功能 (feat)
200
+ - [ ] Bug 修复 (fix)
201
+ - [ ] 重构 (refactor)
202
+ - [ ] 性能优化 (perf)
203
+
204
+ ## 测试验证
205
+ - [ ] 单元测试通过
206
+ - [ ] 手动测试场景: [描述]
207
+ - [ ] 截图/录屏(UI 变更必填)
208
+
209
+ ## 影响范围
210
+ [描述可能影响的模块或依赖方]
211
+
212
+ ## Checklist
213
+ - [ ] 代码自查完毕
214
+ - [ ] 无调试代码 (console.log/debugger)
215
+ - [ ] 文档已更新(如需要)
216
+ \`\`\`
217
+
218
+ **PR 规模控制**
219
+ - 理想 PR 大小:< 400 行(不含测试)
220
+ - 超过 800 行:强制拆分为多个 PR
221
+ - 可用 \`git diff --stat origin/main\` 提前检查
222
+
223
+ **分支保护规则(GitHub/GitLab 配置)**
224
+ \`\`\`
225
+ main 分支保护:
226
+ ✅ Require pull request reviews (min: 1)
227
+ ✅ Require status checks to pass (CI/lint/test)
228
+ ✅ Require branches to be up to date
229
+ ✅ Restrict push access (仅管理员)
230
+ ✅ Require signed commits(高安全场景)
231
+ \`\`\``,
232
+ },
233
+ {
234
+ title: '6. 输出工作流规范文档',
235
+ content: `整理为团队可直接使用的规范文档,格式如下:
236
+
237
+ \`\`\`markdown
238
+ ## Git 工作流规范
239
+
240
+ ### 分支命名
241
+ - feature/<ticket-id>-short-description (如: feature/PROJ-123-user-login)
242
+ - fix/<ticket-id>-short-description
243
+ - hotfix/<version>-short-description (如: hotfix/1.2.1-payment-crash)
244
+ - release/<version> (如: release/1.3.0)
245
+
246
+ ### 提交规范
247
+ 格式: <type>(<scope>): <subject>
248
+ 示例: feat(auth): add JWT refresh token support
249
+
250
+ ### 禁止行为
251
+ ❌ 直接推送到 main/master
252
+ ❌ force push 到共享分支
253
+ ❌ rebase 已推送的公共分支
254
+ ❌ 超过 1000 行的单次 PR(紧急 hotfix 除外)
255
+
256
+ ### 分支生命周期
257
+ - feature 分支: ≤ 5 个工作日
258
+ - release 分支: ≤ 2 周
259
+ - hotfix 分支: ≤ 24 小时
260
+ \`\`\``,
261
+ },
262
+ ],
263
+ outputFormat: 'Markdown 工作流规范文档,含分支策略选型建议、提交规范示例、rebase/merge 决策指南、冲突解决 SOP 和 PR 规范模板',
264
+ examples: [
265
+ {
266
+ input: '我们团队5人,做 SaaS 产品,每周发版一次,应该用什么 Git 工作流?',
267
+ output: `## Git 工作流建议:GitHub Flow(简化 GitFlow)
268
+
269
+ **推荐理由**:5人小团队 + 每周发版,GitHub Flow 复杂度适中,无需维护多个长期分支。
270
+
271
+ **分支结构**
272
+ - \`main\`:始终可部署,受保护
273
+ - \`feature/xxx\`:功能开发,完成即 PR,合并即删除
274
+
275
+ **提交规范**:Conventional Commits
276
+ **合并策略**:Squash Merge(保持 main 历史干净)
277
+ **发版标记**:\`git tag v1.x.0\` 打标签`,
278
+ },
279
+ ],
280
+ notes: [
281
+ '分支策略没有银弹,根据团队规模和发版频率选择最适合的',
282
+ 'force push 操作必须在团队内公告,避免其他成员本地分支混乱',
283
+ '建议在 CI 中自动校验 commit message 格式,而非依赖人工审查',
284
+ '冲突解决后务必运行测试,确保合并结果功能正常',
285
+ ],
286
+ nextSkill: 'unit-testing',
287
+ };
288
+ //# sourceMappingURL=15-git-workflow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"15-git-workflow.js","sourceRoot":"","sources":["../../src/skills/15-git-workflow.ts"],"names":[],"mappings":";;;AAEa,QAAA,gBAAgB,GAAoB;IAC/C,EAAE,EAAE,cAAc;IAClB,IAAI,EAAE,SAAS;IACf,MAAM,EAAE,cAAc;IACtB,KAAK,EAAE,EAAE;IACT,QAAQ,EAAE,KAAK;IACf,WAAW,EAAE,sCAAsC;IACnD,aAAa,EAAE,+FAA+F;IAC9G,iBAAiB,EAAE;2DACsC;IACzD,QAAQ,EAAE;QACR,SAAS;QACT,cAAc;QACd,QAAQ;QACR,MAAM;QACN,oBAAoB;QACpB,WAAW;QACX,mBAAmB;QACnB,MAAM;QACN,OAAO;QACP,iBAAiB;QACjB,MAAM;QACN,YAAY;QACZ,qBAAqB;KACtB;IACD,KAAK,EAAE;QACL;YACE,KAAK,EAAE,kBAAkB;YACzB,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BAgCW;SACrB;QACD;YACE,KAAK,EAAE,mCAAmC;YAC1C,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAuCR;SACF;QACD;YACE,KAAK,EAAE,0BAA0B;YACjC,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gCAuCiB;SAC3B;QACD;YACE,KAAK,EAAE,WAAW;YAClB,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAmCR;SACF;QACD;YACE,KAAK,EAAE,oCAAoC;YAC3C,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAsCR;SACF;QACD;YACE,KAAK,EAAE,cAAc;YACrB,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;OAyBR;SACF;KACF;IACD,YAAY,EACV,wEAAwE;IAC1E,QAAQ,EAAE;QACR;YACE,KAAK,EAAE,wCAAwC;YAC/C,MAAM,EAAE;;;;;;;;;;gCAUkB;SAC3B;KACF;IACD,KAAK,EAAE;QACL,4BAA4B;QAC5B,oCAAoC;QACpC,yCAAyC;QACzC,wBAAwB;KACzB;IACD,SAAS,EAAE,cAAc;CAC1B,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { SkillDefinition } from './types';
2
+ export declare const unitTestingSkill: SkillDefinition;
3
+ //# sourceMappingURL=16-unit-testing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"16-unit-testing.d.ts","sourceRoot":"","sources":["../../src/skills/16-unit-testing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,eAAO,MAAM,gBAAgB,EAAE,eAsS9B,CAAC"}
@@ -0,0 +1,298 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.unitTestingSkill = void 0;
4
+ exports.unitTestingSkill = {
5
+ id: 'unit-testing',
6
+ name: '单元测试',
7
+ nameEn: 'unit_testing',
8
+ order: 16,
9
+ category: '质量侧',
10
+ description: '运用 AAA 模式和 TDD 工作流编写高质量单元测试,建立覆盖率目标和 Mock 策略',
11
+ descriptionEn: 'Write high-quality unit tests using AAA pattern, TDD workflow, mocking strategies, and coverage goals',
12
+ detailDescription: `系统指导单元测试的设计与实现,涵盖 AAA(Arrange-Act-Assert)模式、TDD 红绿重构循环、
13
+ Mock/Stub/Spy 策略选择、覆盖率目标制定和测试命名规范,帮助团队建立可维护、有效的测试体系。`,
14
+ triggers: [
15
+ '单元测试',
16
+ 'unit test',
17
+ '写测试',
18
+ 'write tests',
19
+ 'TDD',
20
+ '测试设计',
21
+ 'test design',
22
+ 'mock 策略',
23
+ 'mocking',
24
+ '测试覆盖率',
25
+ 'coverage',
26
+ '@ethan test',
27
+ '@ethan unit-testing',
28
+ ],
29
+ steps: [
30
+ {
31
+ title: '1. 明确测试目标与范围',
32
+ content: `在编写测试前,先明确测什么:
33
+
34
+ **测试金字塔**
35
+ \`\`\`
36
+ ┌───────────┐
37
+ │ E2E 测试 │ (少量,慢,高置信)
38
+ ┌┴───────────┴┐
39
+ │ 集成测试 │ (适量,中速)
40
+ ┌┴─────────────┴┐
41
+ │ 单元测试 │ (大量,快,低成本)
42
+ └───────────────┘
43
+ \`\`\`
44
+
45
+ **单元测试应该覆盖**
46
+ - ✅ 纯函数的各种输入输出(含边界)
47
+ - ✅ 类/模块的公共方法逻辑
48
+ - ✅ 条件分支(if/switch/三元)
49
+ - ✅ 错误处理路径(throw/catch)
50
+ - ✅ 异步操作(Promise/async-await)
51
+
52
+ **不应该单元测试**
53
+ - ❌ 简单的 getter/setter(无逻辑)
54
+ - ❌ 第三方库内部实现
55
+ - ❌ 框架本身(如 React 渲染机制)
56
+ - ❌ 私有方法(通过公共方法间接测试)`,
57
+ },
58
+ {
59
+ title: '2. AAA 模式编写测试用例',
60
+ content: `每个测试用例遵循 **Arrange → Act → Assert** 三段式结构:
61
+
62
+ **基础示例(JavaScript/TypeScript with Vitest/Jest)**
63
+ \`\`\`typescript
64
+ describe('calculateDiscount', () => {
65
+ it('should apply 20% discount for premium users', () => {
66
+ // Arrange(准备:设置测试数据和依赖)
67
+ const user = { type: 'premium', cart: [{ price: 100 }, { price: 50 }] };
68
+ const expectedTotal = 120; // 150 * 0.8
69
+
70
+ // Act(执行:调用被测函数)
71
+ const result = calculateDiscount(user);
72
+
73
+ // Assert(断言:验证结果)
74
+ expect(result.total).toBe(expectedTotal);
75
+ expect(result.discountRate).toBe(0.2);
76
+ });
77
+ });
78
+ \`\`\`
79
+
80
+ **测试命名规范(Given-When-Then)**
81
+ \`\`\`typescript
82
+ // 格式: should <expected behavior> when <condition>
83
+ it('should return null when user is not found')
84
+ it('should throw AuthError when token is expired')
85
+ it('should apply 20% discount when user has premium status')
86
+
87
+ // 或使用 Given-When-Then 风格
88
+ it('given empty cart, when checkout, then throws EmptyCartError')
89
+ \`\`\`
90
+
91
+ **边界条件测试清单**
92
+ \`\`\`typescript
93
+ describe('parseAge', () => {
94
+ // 正常值
95
+ it('should parse valid age 25')
96
+ // 边界值
97
+ it('should accept minimum age 0')
98
+ it('should accept maximum age 150')
99
+ // 非法值
100
+ it('should throw when age is negative')
101
+ it('should throw when age exceeds 150')
102
+ // 类型边界
103
+ it('should throw when age is not a number')
104
+ it('should throw when age is null or undefined')
105
+ it('should handle decimal by flooring to integer')
106
+ });
107
+ \`\`\``,
108
+ },
109
+ {
110
+ title: '3. TDD 工作流(红-绿-重构)',
111
+ content: `**TDD 循环步骤**
112
+
113
+ \`\`\`
114
+ 🔴 Red → 写一个失败的测试(先设计接口)
115
+ 🟢 Green → 写最少代码让测试通过(不过度设计)
116
+ 🔵 Refactor → 在测试保护下重构代码
117
+ \`\`\`
118
+
119
+ **实践示例:用 TDD 实现邮箱验证**
120
+
121
+ \`\`\`typescript
122
+ // Step 1 🔴 先写测试(此时 validateEmail 还不存在)
123
+ describe('validateEmail', () => {
124
+ it('should return true for valid email', () => {
125
+ expect(validateEmail('user@example.com')).toBe(true);
126
+ });
127
+ it('should return false for missing @', () => {
128
+ expect(validateEmail('userexample.com')).toBe(false);
129
+ });
130
+ it('should return false for empty string', () => {
131
+ expect(validateEmail('')).toBe(false);
132
+ });
133
+ });
134
+
135
+ // Step 2 🟢 写最简实现让测试通过
136
+ export function validateEmail(email: string): boolean {
137
+ return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
138
+ }
139
+
140
+ // Step 3 🔵 重构:提取正则为常量,添加类型注释
141
+ const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
142
+ export function validateEmail(email: string): boolean {
143
+ if (!email) return false;
144
+ return EMAIL_REGEX.test(email);
145
+ }
146
+ \`\`\`
147
+
148
+ **TDD 适用场景**
149
+ - 明确需求的业务逻辑函数
150
+ - 工具库/SDK 开发
151
+ - Bug 修复(先写复现测试再修复)
152
+
153
+ **不强制 TDD 的场景**
154
+ - 探索性开发阶段
155
+ - UI 组件(先实现再补测试)`,
156
+ },
157
+ {
158
+ title: '4. Mock / Stub / Spy 策略',
159
+ content: `**三种测试替身的区别**
160
+
161
+ | 类型 | 用途 | 验证方式 |
162
+ |------|------|---------|
163
+ | **Stub** | 替换外部依赖,控制返回值 | 只验证输出 |
164
+ | **Mock** | 验证函数是否被正确调用 | 验证调用行为 |
165
+ | **Spy** | 监听真实函数的调用情况 | 包装真实实现 |
166
+
167
+ **Vitest/Jest 实践**
168
+ \`\`\`typescript
169
+ import { vi, describe, it, expect, beforeEach } from 'vitest';
170
+
171
+ // Stub: 控制外部 API 返回值
172
+ vi.mock('../api/user', () => ({
173
+ fetchUser: vi.fn().mockResolvedValue({ id: 1, name: 'Alice' }),
174
+ }));
175
+
176
+ // Mock: 验证函数被调用
177
+ it('should call sendEmail when user registers', async () => {
178
+ const sendEmail = vi.fn();
179
+ await registerUser({ email: 'test@test.com' }, { sendEmail });
180
+ expect(sendEmail).toHaveBeenCalledOnce();
181
+ expect(sendEmail).toHaveBeenCalledWith('test@test.com', expect.objectContaining({ subject: 'Welcome' }));
182
+ });
183
+
184
+ // Spy: 包装真实函数监听
185
+ it('should log error when fetch fails', async () => {
186
+ const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
187
+ vi.mocked(fetchUser).mockRejectedValue(new Error('Network Error'));
188
+ await loadUserProfile(1);
189
+ expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('Network Error'));
190
+ consoleSpy.mockRestore();
191
+ });
192
+ \`\`\`
193
+
194
+ **Mock 黄金法则**
195
+ - 只 Mock 跨边界的依赖(网络、数据库、文件系统、时间)
196
+ - 不要 Mock 被测单元的内部实现
197
+ - 每次测试后还原 Mock(使用 \`beforeEach(() => vi.clearAllMocks())\`)`,
198
+ },
199
+ {
200
+ title: '5. 覆盖率目标与质量保障',
201
+ content: `**覆盖率类型与目标**
202
+
203
+ | 覆盖率类型 | 说明 | 建议目标 |
204
+ |----------|------|---------|
205
+ | 语句覆盖(Statements) | 执行的语句比例 | ≥ 80% |
206
+ | 分支覆盖(Branches) | if/else 分支比例 | ≥ 75% |
207
+ | 函数覆盖(Functions) | 调用的函数比例 | ≥ 80% |
208
+ | 行覆盖(Lines) | 执行的代码行比例 | ≥ 80% |
209
+
210
+ **Vitest 覆盖率配置**
211
+ \`\`\`typescript
212
+ // vitest.config.ts
213
+ export default defineConfig({
214
+ test: {
215
+ coverage: {
216
+ provider: 'v8', // 或 'istanbul'
217
+ reporter: ['text', 'html', 'lcov'],
218
+ thresholds: {
219
+ statements: 80,
220
+ branches: 75,
221
+ functions: 80,
222
+ lines: 80,
223
+ },
224
+ exclude: [
225
+ 'node_modules/',
226
+ 'src/types/',
227
+ '**/*.config.*',
228
+ '**/*.d.ts',
229
+ ],
230
+ },
231
+ },
232
+ });
233
+ \`\`\`
234
+
235
+ **覆盖率反模式(要避免)**
236
+ \`\`\`typescript
237
+ // ❌ 为了覆盖率写无意义断言
238
+ it('does something', () => {
239
+ expect(doSomething()).toBeDefined(); // 没有验证具体行为
240
+ });
241
+
242
+ // ✅ 验证真实业务逻辑
243
+ it('should return correct discounted price', () => {
244
+ expect(calculatePrice(100, 0.1)).toBe(90);
245
+ });
246
+ \`\`\`
247
+
248
+ **CI 集成**
249
+ \`\`\`yaml
250
+ # .github/workflows/test.yml
251
+ - name: Run tests with coverage
252
+ run: npm run test -- --coverage
253
+
254
+ - name: Comment coverage on PR
255
+ uses: MishaKav/jest-coverage-comment@main
256
+ with:
257
+ coverage-summary-path: ./coverage/coverage-summary.json
258
+ \`\`\``,
259
+ },
260
+ ],
261
+ outputFormat: 'Markdown 测试方案文档,含测试用例设计(AAA 格式)、Mock 策略说明、覆盖率目标和 CI 配置示例',
262
+ examples: [
263
+ {
264
+ input: '帮我为这个 calculateTax 函数写单元测试',
265
+ output: `## 单元测试方案:calculateTax
266
+
267
+ \`\`\`typescript
268
+ describe('calculateTax', () => {
269
+ describe('standard rate (20%)', () => {
270
+ it('should apply 20% tax to positive amount', () => {
271
+ expect(calculateTax(100, 'standard')).toBe(20);
272
+ });
273
+ it('should return 0 tax for zero amount', () => {
274
+ expect(calculateTax(0, 'standard')).toBe(0);
275
+ });
276
+ });
277
+
278
+ describe('error cases', () => {
279
+ it('should throw when amount is negative', () => {
280
+ expect(() => calculateTax(-1, 'standard')).toThrow('Amount must be positive');
281
+ });
282
+ it('should throw for unknown tax category', () => {
283
+ expect(() => calculateTax(100, 'unknown' as any)).toThrow('Unknown tax category');
284
+ });
285
+ });
286
+ });
287
+ \`\`\``,
288
+ },
289
+ ],
290
+ notes: [
291
+ '测试应该是自文档化的,好的测试名称比注释更有价值',
292
+ '避免测试实现细节,测试行为而非内部结构,有助于重构时测试不频繁失败',
293
+ '不要追求 100% 覆盖率,关注核心业务逻辑的质量覆盖',
294
+ '测试代码同样需要维护,避免过度复杂的测试辅助函数',
295
+ ],
296
+ nextSkill: 'system-design',
297
+ };
298
+ //# sourceMappingURL=16-unit-testing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"16-unit-testing.js","sourceRoot":"","sources":["../../src/skills/16-unit-testing.ts"],"names":[],"mappings":";;;AAEa,QAAA,gBAAgB,GAAoB;IAC/C,EAAE,EAAE,cAAc;IAClB,IAAI,EAAE,MAAM;IACZ,MAAM,EAAE,cAAc;IACtB,KAAK,EAAE,EAAE;IACT,QAAQ,EAAE,KAAK;IACf,WAAW,EAAE,8CAA8C;IAC3D,aAAa,EAAE,uGAAuG;IACtH,iBAAiB,EAAE;qDACgC;IACnD,QAAQ,EAAE;QACR,MAAM;QACN,WAAW;QACX,KAAK;QACL,aAAa;QACb,KAAK;QACL,MAAM;QACN,aAAa;QACb,SAAS;QACT,SAAS;QACT,OAAO;QACP,UAAU;QACV,aAAa;QACb,qBAAqB;KACtB;IACD,KAAK,EAAE;QACL;YACE,KAAK,EAAE,cAAc;YACrB,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;qBAwBM;SAChB;QACD;YACE,KAAK,EAAE,iBAAiB;YACxB,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+CR;SACF;QACD;YACE,KAAK,EAAE,oBAAoB;YAC3B,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA4CE;SACZ;QACD;YACE,KAAK,EAAE,yBAAyB;YAChC,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4DAsC6C;SACvD;QACD;YACE,KAAK,EAAE,eAAe;YACtB,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAyDR;SACF;KACF;IACD,YAAY,EACV,0DAA0D;IAC5D,QAAQ,EAAE;QACR;YACE,KAAK,EAAE,4BAA4B;YACnC,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;OAsBP;SACF;KACF;IACD,KAAK,EAAE;QACL,0BAA0B;QAC1B,mCAAmC;QACnC,6BAA6B;QAC7B,0BAA0B;KAC3B;IACD,SAAS,EAAE,eAAe;CAC3B,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { SkillDefinition } from './types';
2
+ export declare const systemDesignSkill: SkillDefinition;
3
+ //# sourceMappingURL=17-system-design.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"17-system-design.d.ts","sourceRoot":"","sources":["../../src/skills/17-system-design.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,eAAO,MAAM,iBAAiB,EAAE,eAkS/B,CAAC"}