coderio 0.4.0__py3-none-any.whl
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.
- coderio/__init__.py +14 -0
- coderio/agent/__init__.py +5 -0
- coderio/agent/_deepagents_compat.py +139 -0
- coderio/agent/command_review.py +140 -0
- coderio/agent/deep_loop.py +758 -0
- coderio/agent/harness.py +768 -0
- coderio/agent/harness_middleware.py +293 -0
- coderio/agent/permission_middleware.py +52 -0
- coderio/agent/prompts.py +304 -0
- coderio/agent/skill_tool.py +70 -0
- coderio/agent/state.py +144 -0
- coderio/agent/stream.py +93 -0
- coderio/cli/__init__.py +0 -0
- coderio/cli/app.py +257 -0
- coderio/cli/commands.py +339 -0
- coderio/cli/credentials.py +70 -0
- coderio/cli/mcp_cmd.py +227 -0
- coderio/cli/multimodal.py +94 -0
- coderio/cli/onboarding.py +270 -0
- coderio/cli/providers.py +113 -0
- coderio/cli/render.py +28 -0
- coderio/cli/repl.py +189 -0
- coderio/cli/run_cmd.py +171 -0
- coderio/cli/skills_cmd.py +77 -0
- coderio/cli/stream.py +224 -0
- coderio/cli/tui.py +1584 -0
- coderio/cli/tui_onboarding.py +465 -0
- coderio/cli/tui_screens.py +334 -0
- coderio/cli/tui_widgets.py +453 -0
- coderio/config/__init__.py +21 -0
- coderio/config/bootstrap.py +38 -0
- coderio/config/loader.py +273 -0
- coderio/config/models.py +193 -0
- coderio/config/trust.py +136 -0
- coderio/llm/__init__.py +3 -0
- coderio/llm/factory.py +164 -0
- coderio/llm/probe.py +153 -0
- coderio/mcp_loader.py +274 -0
- coderio/session/__init__.py +3 -0
- coderio/session/message.py +79 -0
- coderio/session/store.py +244 -0
- coderio/skills/__init__.py +4 -0
- coderio/skills/bundled/executing-plans/SKILL.md +15 -0
- coderio/skills/lion-skills/LICENSE +21 -0
- coderio/skills/lion-skills/README.md +114 -0
- coderio/skills/lion-skills/skills/clarifying-questions/SKILL.md +119 -0
- coderio/skills/lion-skills/skills/clarifying-questions/evals/evals.json +40 -0
- coderio/skills/lion-skills/skills/code-review/SKILL.md +102 -0
- coderio/skills/lion-skills/skills/code-review/evals/evals.json +40 -0
- coderio/skills/lion-skills/skills/commit-message/SKILL.md +97 -0
- coderio/skills/lion-skills/skills/commit-message/evals/evals.json +20 -0
- coderio/skills/lion-skills/skills/debugging/SKILL.md +116 -0
- coderio/skills/lion-skills/skills/debugging/evals/evals.json +40 -0
- coderio/skills/lion-skills/skills/error-handling/SKILL.md +74 -0
- coderio/skills/lion-skills/skills/error-handling/evals/evals.json +20 -0
- coderio/skills/lion-skills/skills/lion-writing-skills/SKILL.md +85 -0
- coderio/skills/lion-skills/skills/lion-writing-skills/evals/evals.json +20 -0
- coderio/skills/lion-skills/skills/naming/SKILL.md +85 -0
- coderio/skills/lion-skills/skills/naming/evals/evals.json +20 -0
- coderio/skills/lion-skills/skills/onboarding-unknown-codebase/SKILL.md +95 -0
- coderio/skills/lion-skills/skills/onboarding-unknown-codebase/evals/evals.json +20 -0
- coderio/skills/lion-skills/skills/spec-writing/SKILL.md +154 -0
- coderio/skills/lion-skills/skills/spec-writing/evals/evals.json +40 -0
- coderio/skills/lion-skills/skills/task-breakdown/SKILL.md +134 -0
- coderio/skills/lion-skills/skills/task-breakdown/evals/evals.json +46 -0
- coderio/skills/lion-skills/skills/testing/SKILL.md +134 -0
- coderio/skills/lion-skills/skills/testing/evals/evals.json +40 -0
- coderio/skills/lion-skills/skills/verify-and-fix/SKILL.md +132 -0
- coderio/skills/lion-skills/skills/verify-and-fix/evals/evals.json +40 -0
- coderio/skills/models.py +66 -0
- coderio/skills/parser.py +50 -0
- coderio/skills/store.py +102 -0
- coderio/tools/__init__.py +94 -0
- coderio/tools/base.py +55 -0
- coderio/tools/bash.py +168 -0
- coderio/tools/command_policy.py +364 -0
- coderio/tools/edit_file.py +55 -0
- coderio/tools/glob_tool.py +21 -0
- coderio/tools/grep_tool.py +90 -0
- coderio/tools/linux_sandbox.py +196 -0
- coderio/tools/list_dir.py +58 -0
- coderio/tools/multi_edit.py +62 -0
- coderio/tools/note.py +72 -0
- coderio/tools/permission.py +214 -0
- coderio/tools/read_file.py +51 -0
- coderio/tools/sandbox_runner.py +113 -0
- coderio/tools/todo.py +71 -0
- coderio/tools/web_fetch.py +188 -0
- coderio/tools/web_search.py +49 -0
- coderio/tools/win_job.py +214 -0
- coderio/tools/win_sandbox.py +639 -0
- coderio/tools/write_file.py +25 -0
- coderio-0.4.0.dist-info/METADATA +490 -0
- coderio-0.4.0.dist-info/RECORD +97 -0
- coderio-0.4.0.dist-info/WHEEL +4 -0
- coderio-0.4.0.dist-info/entry_points.txt +2 -0
- coderio-0.4.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: code-review
|
|
3
|
+
description: 审查代码(自己的或别人的),需有重点、分层次地看时。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Code Review
|
|
7
|
+
|
|
8
|
+
## 概述
|
|
9
|
+
|
|
10
|
+
对代码做**有重点、分层次**的审查——先抓正确性和安全问题,再管可读性,风格交给工具。核心:好的 review 让作者知道"哪里有真问题、该先改什么";坏的 review 是一堆 nit 淹没重点,或一句"看起来不错"放过错误。
|
|
11
|
+
|
|
12
|
+
## 何时使用
|
|
13
|
+
|
|
14
|
+
- 提交前审查自己或别人的代码(自我 review 见下文专门技巧)
|
|
15
|
+
- 同事让你看他的 PR
|
|
16
|
+
- 不确定 review 该看什么、怎么反馈
|
|
17
|
+
|
|
18
|
+
**不该用**:纯风格/格式问题(交给 linter/formatter 自动化,不该占用人 review);想确认"功能对不对"——那是 verify-and-fix(实际跑)的事,review 是静态审查、替代不了运行验证。
|
|
19
|
+
|
|
20
|
+
**与相邻 skill 的边界**:`code-review` 是**审查视角**(找问题、分严重度、给建议),`verify-and-fix` 是**修复视角**(改对、验证)。code-review 发现的问题,需要修的进入 verify-and-fix 流程;review 中发现的"行为不对/报错"可能要先用 `debugging` 定位根因。三者接力:review 找问题 → debugging 定位 → verify-and-fix 修+验证。
|
|
21
|
+
|
|
22
|
+
## 核心内容
|
|
23
|
+
|
|
24
|
+
### 正确性优先,别被风格带偏
|
|
25
|
+
|
|
26
|
+
review 最常见的错是**逐行挑风格**(命名、缩进、注释多少)而放过**正确性**(逻辑对不对、边界处理了吗、有没有竞态)。正确性问题会让程序出错,风格问题只是不好看——前者 critical,后者 nit,优先级天差地别。
|
|
27
|
+
|
|
28
|
+
按重要性排序,review 该查的维度:
|
|
29
|
+
|
|
30
|
+
1. **正确性**:逻辑对吗?能跑通预期路径吗?有没有逻辑漏洞?(最该花时间)
|
|
31
|
+
2. **边界与异常**:空值/空集合/零/负数/超大输入怎么处理?外部依赖失败(网络/DB)呢?
|
|
32
|
+
3. **安全**:有没有注入(SQL/命令/XSS)?密码/密钥处理对吗?权限检查到位吗?敏感信息泄露吗?
|
|
33
|
+
4. **并发**:共享状态有竞态吗?异步顺序依赖对吗?资源泄漏(未关闭的连接/锁未释放)?
|
|
34
|
+
5. **可维护性**:命名清不清楚?结构是否过度复杂?有没有重复?(这里才是 nit 的领地)
|
|
35
|
+
6. **测试**:有测试吗?测的是行为还是实现?覆盖了关键路径和边界吗?
|
|
36
|
+
|
|
37
|
+
前 4 类是"会让程序出错或出事"的,必须查;第 5 类是"让人难受"的,次要;风格细节(缩进/格式)不该人查,交给工具。
|
|
38
|
+
|
|
39
|
+
**按改动性质调整重点**:上面是通用清单,但不同代码该重点查的不同——别对所有代码平均用力。涉及**钱/库存/计数**的,重点查原子性和一致性(中途失败会不会凭空产生/消失);涉及**外部输入**的(用户输入、API、文件),重点查安全(注入、越权);涉及**共享状态/异步**的,重点查竞态和资源泄漏;涉及**配置/迁移**的,重点查回滚和兼容。先识别"这段代码的风险面在哪",把 review 力量集中投到那里。
|
|
40
|
+
|
|
41
|
+
### 分严重度,别把 nit 和 critical 混着
|
|
42
|
+
|
|
43
|
+
review 反馈必须**分严重度**,让作者知道先改什么:
|
|
44
|
+
|
|
45
|
+
- **阻塞(blocking / critical)**:必须改才能合并——逻辑错、安全漏洞、会崩溃、数据丢失风险。
|
|
46
|
+
- **重要(important)**:强烈建议改——边界没处理、缺少测试、设计有隐患,但不阻塞本次合并。
|
|
47
|
+
- **建议(nit / suggestion)**:可选——命名、可读性、小重构。改了更好,不改也能过。
|
|
48
|
+
|
|
49
|
+
不分层的 review 有两种失败:把 nit 当 critical(作者被一堆小事压垮,反而漏改真问题)、把 critical 当 nit(真问题被淹没在风格意见里)。**nit 要克制**——堆 15 条 nit 是在浪费作者时间,把同类 nit 归并成一条"风格建议",或直接交给 linter。
|
|
50
|
+
|
|
51
|
+
### 反馈要带依据和建议,不只是"这不好"
|
|
52
|
+
|
|
53
|
+
每条 review 意见应该让作者能**理解问题 + 知道怎么改**:
|
|
54
|
+
|
|
55
|
+
- **指出问题 + 为什么是问题**:不说"这写得不好",说"`user.name` 在 user 为 null 时会抛 TypeError——db.find 找不到时返回 null"。
|
|
56
|
+
- **给方向或示例**:不只说"改一下",给"加个 null 检查,找不到时抛业务错误或返回 null,看调用方期望"。
|
|
57
|
+
- **区分事实和偏好**:"这里有 null 风险"(事实,基于代码)vs "我觉得该用 early return"(偏好,标注是建议)。
|
|
58
|
+
|
|
59
|
+
带依据的反馈让作者能判断对错、学到东西;空泛的"不好"让作者只能盲从或抵触。
|
|
60
|
+
|
|
61
|
+
### 别只看 diff,要看整体
|
|
62
|
+
|
|
63
|
+
review 容易陷入"逐行看 diff",但很多问题在**整体层面**才看得出来:
|
|
64
|
+
|
|
65
|
+
- 这个改动**和系统的其他部分一致吗**(命名约定、错误处理风格、架构分层)?
|
|
66
|
+
- 改动**有没有破坏既有契约**(改了函数签名,调用方都更新了吗)?
|
|
67
|
+
- **缺了什么**(diff 里只有 happy path,错误处理呢?只有功能代码,测试呢?)?
|
|
68
|
+
|
|
69
|
+
diff 告诉你"改了什么",但要结合"整体该怎么"才能看出"改对了吗、改全了吗"。
|
|
70
|
+
|
|
71
|
+
### 大 PR:分块 + 抓主线,别试图一口气读完
|
|
72
|
+
|
|
73
|
+
大改动(几百行、多文件)的 review 容易陷入"看不完、看到后面忘前面"。策略:
|
|
74
|
+
|
|
75
|
+
- **先读 PR 描述/commit message**:作者说这次改了什么、为什么——这是主线,review 时随时对照"改动是否服务这个主线、有没有跑题"。
|
|
76
|
+
- **按逻辑分块,不按文件**:把改动按"功能模块"分组(如"认证部分""数据迁移""UI"),一块一块审,每块审完总结"这块改了啥、有没有问题",再下一块。
|
|
77
|
+
- **抓主线正确性,细节分层**:先确认主线逻辑通(核心路径对不对),再下沉到边界/异常。主线错了一切白搭;主线对了再逐块找细节问题。
|
|
78
|
+
- **太大就要求拆**:如果一个 PR 改了互不相关的多件事,要求作者拆成几个 PR——大而杂的 PR 几乎无法有效 review,这不是 reviewer 的问题,是 PR 的问题。
|
|
79
|
+
|
|
80
|
+
### 自我 review:换视角,先跑后看
|
|
81
|
+
|
|
82
|
+
提交前自查自己的代码,有两个反直觉但有效的技巧:
|
|
83
|
+
|
|
84
|
+
- **放一会儿再看 / 换视角**:刚写完时代码在大脑的"短期记忆"里,你会自动补全没写好的地方、跳过自己的盲点。放几小时(或睡一觉)再看,或假装在 review 别人的代码——视角一换,自己的问题就显形了。
|
|
85
|
+
- **先用工具跑,再用人眼看**:先跑测试/lint/类型检查,让工具抓住机械问题(编译错、类型错、明显的 lint);人眼集中查工具抓不到的——逻辑对不对、边界处理了吗、命名清不清楚。工具和人各查擅长的,别让人干工具的活。
|
|
86
|
+
- **对照 commit message 自查**:你这次提交说"修了 X",那 diff 里应该只有修 X 相关的改动——混进去的无关改动(顺手重构、调试代码)会被这个对照揪出来。
|
|
87
|
+
|
|
88
|
+
自我 review 的产出标准同对外 review:**说出查了什么维度、发现什么**,而不是"我看过了没问题"。
|
|
89
|
+
|
|
90
|
+
## 常见错误
|
|
91
|
+
|
|
92
|
+
| 问题 | 修法 |
|
|
93
|
+
|------|------|
|
|
94
|
+
| 表面附和"看起来不错" | 列出实际查了哪些维度、发现什么,空 review 等于没 review |
|
|
95
|
+
| 逐行挑 nit 淹没正确性 | 正确性/安全/边界优先,nit 克制并归并 |
|
|
96
|
+
| 不分严重度,nit 和 critical 混着 | 分阻塞/重要/建议三层,让作者知道先改什么 |
|
|
97
|
+
| 只说"不好"不说怎么改 | 每条带"为什么是问题 + 改的方向/示例" |
|
|
98
|
+
| 只看 diff 逐行 | 结合整体:一致性、契约破坏、缺了什么 |
|
|
99
|
+
| 风格问题占用人 review | 交给 linter/formatter,人查判断性问题 |
|
|
100
|
+
| 只看功能不看测试 | 查测试是否存在、测的是行为还是实现、覆盖关键路径 |
|
|
101
|
+
| 大 PR 一口气读,读到后面忘前面 | 先读描述抓主线,按逻辑分块审,太大就要求拆 PR |
|
|
102
|
+
| 自我 review 刚写完就看 | 放一会儿/换视角,先用工具跑再人眼看,对照 commit message 查混入的无关改动 |
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill_name": "code-review",
|
|
3
|
+
"evals": [
|
|
4
|
+
{
|
|
5
|
+
"id": 1,
|
|
6
|
+
"prompt": "帮我 review 一下这段代码: async function getUser(id) { const user = await db.find(id); return user.name; }",
|
|
7
|
+
"expected": "不只看风格,重点查正确性(user 可能 null,user.name 抛错=blocking)。按严重度分层(null 问题 critical 排最前)。给具体建议(加 null 检查/抛业务错)。不用 nit 淹没真问题。按改动性质调整重点(外部依赖 db→重点查边界/异常)"
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"id": 2,
|
|
11
|
+
"prompt": "这个 PR 加了个登录功能,代码大概 200 行。我让你 review,你说'看起来不错,逻辑清晰'。这样 review 够吗?",
|
|
12
|
+
"expected": "识别表面附和式 review('看起来不错'没实际检查,空 review 等于没 review)。指出有效 review 要查具体维度(正确性/边界/安全/并发/测试)。针对登录点出该查的安全点(密码哈希/限流防爆破/SQL参数化/错误信息泄露)。强调不能只说不错要指出查了什么发现什么"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"id": 3,
|
|
16
|
+
"prompt": "我 review 同事的代码,挑了一堆问题:变量名不规范、注释少了点、缩进不一致、函数有点长、可以提取个常量。我列了 15 条让他改。这样 review 好吗?",
|
|
17
|
+
"expected": "识别逐行挑 nit(15 条全风格,淹没正确性/安全)。指出 review 要分严重度,nit 不该和 critical 混。建议聚焦 critical/重要(逻辑/安全/边界),nit 归并或交 linter。区分该人 review 的(判断性) vs 该工具做的(机械风格)"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"id": 4,
|
|
21
|
+
"prompt": "同事提了个 PR,改了 30 个文件、800 行,既有重构又有新功能还有 bugfix 混在一起。我一看就头大,不知从哪审。怎么办?",
|
|
22
|
+
"expected": "不附和'硬着头皮读完',给大 PR 分块策略。先读 PR 描述/commit message 抓主线,review 时对照主线。按逻辑分块(功能模块)非按文件,一块审完总结再下块。抓主线正确性优先再下沉细节。太大就要求拆——大而杂 PR 几乎无法有效 review,是 PR 问题非 reviewer 问题"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"id": 5,
|
|
26
|
+
"prompt": "我刚写完一个功能,想提交前自己 review 一遍。我打算从头到尾再读一遍代码看看有没有问题。这样行吗?",
|
|
27
|
+
"expected": "不附和'从头读一遍就行',指出刚写完自我 review 有盲点(代码在短期记忆会自动补全)。建议放一会儿/换视角(假装 review 别人代码)。先用工具跑(测试/lint/类型检查)再人眼看。对照 commit message 自查揪混入的无关改动。产出标准同对外 review(说查了什么发现什么)"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"id": 6,
|
|
31
|
+
"prompt": "帮我 review 一下这段代码: function transfer(from, to, amount) { accounts[from] -= amount; accounts[to] += amount; }",
|
|
32
|
+
"expected": "不只看风格重点查正确性。查关键问题:余额不足没检查(变负)、amount 负/NaN/非数值(资金方向反转/数据损坏)、账户不存在、缺原子性(中途失败钱凭空消失/产生)。按严重度分层 critical 排前。给具体建议。按改动性质调整重点(涉及钱→重点查原子性/一致性+并发竞态)"
|
|
33
|
+
}
|
|
34
|
+
],
|
|
35
|
+
"_validation_history": {
|
|
36
|
+
"round_1": "v1 跑 case 1/2/3:3×4=12 项全过。但发现 v1 缺'大 PR 分块策略''自我 review 技巧'两块",
|
|
37
|
+
"round_2": "v2 跑变体 case 4/5/6:3×4-5=14 项全过,压测两处新增能力(大PR/自我review)全部生效,case6 回归正确性优先保持且 review 质量极高(抓到原子性/竞态深层问题)",
|
|
38
|
+
"round_3": "v3 补'按改动性质调整重点'(发现 v2 维度清单静态、缺针对性);回归 case 1/2/3:3×4-5=14 项全过,确认 v3 生效且未破坏 v1/v2。迭代收敛"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: commit-message
|
|
3
|
+
description: Git 改动需编写规范提交信息时。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Commit Message
|
|
7
|
+
|
|
8
|
+
## 概述
|
|
9
|
+
|
|
10
|
+
把一团 diff 提炼成规范、可追溯的提交信息。核心:一条提交说清"做了什么"和"为什么",让未来的自己(和同事)能从提交历史里读懂项目演进。
|
|
11
|
+
|
|
12
|
+
## 何时使用
|
|
13
|
+
|
|
14
|
+
- 写完代码要提交,需要写 commit message
|
|
15
|
+
- 改了多处,不知怎么概括
|
|
16
|
+
- 现有提交信息太笼统("update""fix bug"),要重写
|
|
17
|
+
- 想统一团队提交规范
|
|
18
|
+
|
|
19
|
+
**不该用**:还没写完代码(先写代码);只想看 diff(用 `git diff`)。
|
|
20
|
+
|
|
21
|
+
## 核心内容
|
|
22
|
+
|
|
23
|
+
### 格式:Conventional Commits
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
<type>(<scope>)<!>: <subject>
|
|
27
|
+
|
|
28
|
+
<body 可选:为什么改>
|
|
29
|
+
|
|
30
|
+
<footer 可选:BREAKING CHANGE: ... / Closes #123>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**破坏性变更**(不向后兼容的改动)两种写法,二选一或并用:
|
|
34
|
+
- **`!` 标记**:紧贴 type/scope 之后、冒号之前,如 `feat(api)!: 改用 token 鉴权替代 cookie`——一眼标红,提醒 reviewer 注意。
|
|
35
|
+
- **脚注 `BREAKING CHANGE:`**:放 footer,写清破坏了什么、怎么迁移,如 `BREAKING CHANGE: /api/login 改为返回 token,前端需改用 Authorization 头`——给受影响方具体的迁移指引。
|
|
36
|
+
|
|
37
|
+
破坏性变更必须二选一明确标注,别藏在普通提交里让下游踩坑。
|
|
38
|
+
|
|
39
|
+
**type 选一个**:`feat`(新功能)、`fix`(修 bug)、`docs`(文档)、`refactor`(重构不改行为)、`test`(测试)、`chore`(构建/杂务)、`perf`(性能)。带可追溯业务变更的迁移脚本(如新增用户表)可算 `feat`,纯基础设施改动算 `chore`。
|
|
40
|
+
|
|
41
|
+
### 从 diff 提炼三步
|
|
42
|
+
|
|
43
|
+
1. **归类**:主要是加功能(feat)、修 bug(fix)、还是重构(refactor)?
|
|
44
|
+
2. **定 scope**:影响哪个模块/文件?(如 `auth`、`api`、`ui`)可省略。
|
|
45
|
+
3. **写 subject**:一行,祈使句,说"做了什么"不说"怎么做的",≤50 字符。
|
|
46
|
+
|
|
47
|
+
### 多处改动怎么办
|
|
48
|
+
|
|
49
|
+
- **相关改动** → 一条提交,body 列要点
|
|
50
|
+
- **不相关改动** → 拆成多条(用 `git add -p` 分块暂存)
|
|
51
|
+
|
|
52
|
+
判断"相不相关":这些改动服务于同一个目的吗?是 → 一条;否 → 拆。
|
|
53
|
+
|
|
54
|
+
**灰色地带——"主线 + 顺带小修复"**:重构或加功能时顺手修了个同处的小 bug(例 1 就是这种),**可以合并**:主线作 subject,bug 修复在 body 一行带过。但若两件事跨文件、跨关注点、或各自都够大,就该拆。一个简单的尺子:**能不能用一句话(一个 subject)概括全部改动?能 → 合;不能 → 拆。**
|
|
55
|
+
|
|
56
|
+
### body 写什么
|
|
57
|
+
|
|
58
|
+
写**为什么**和**影响**,不写代码细节(代码自己会说话):动机/背景、副作用/破坏性变更的迁移说明、关联 issue/PR(如 `Closes #123`)。破坏性变更的标注方式见上面「格式」节。subject 够清楚的小改动可以只写 subject,别硬凑 body。
|
|
59
|
+
|
|
60
|
+
### 语言
|
|
61
|
+
|
|
62
|
+
subject 跟随仓库现有风格(多数中文团队和本仓库用中文 subject;Conventional Commits 生态也常见英文)——没有仓库上下文时跟随团队语言;body 语言同 subject;用户明确偏好优先。
|
|
63
|
+
|
|
64
|
+
### 例子
|
|
65
|
+
|
|
66
|
+
**例 1(单改动)** — 把 useState 换成 useReducer:
|
|
67
|
+
```
|
|
68
|
+
refactor(auth): 用 useReducer 替换登录表单的状态管理
|
|
69
|
+
|
|
70
|
+
复杂的状态转换用 reducer 更清晰,也为后续多步校验做准备。
|
|
71
|
+
顺带修了 useEffect 未清理订阅导致的内存泄漏。
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**例 2(多相关改动)** — 加登录接口 + 测试:
|
|
75
|
+
```
|
|
76
|
+
feat(auth): 实现邮箱密码登录接口
|
|
77
|
+
|
|
78
|
+
- 新增 /api/login 端点
|
|
79
|
+
- 加密码哈希校验
|
|
80
|
+
- 补单元测试覆盖成功/失败场景
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**例 3(该拆的)** — 登录接口、迁移脚本、README 三件不相关事,拆三条:
|
|
84
|
+
```
|
|
85
|
+
feat(auth): 实现登录接口
|
|
86
|
+
chore(db): 添加用户表迁移脚本
|
|
87
|
+
docs: 更新 README 部署说明
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## 常见错误
|
|
91
|
+
|
|
92
|
+
| 问题 | 修法 |
|
|
93
|
+
|------|------|
|
|
94
|
+
| 太笼统("update""改了点") | 写清 type + 具体做了什么 |
|
|
95
|
+
| 一条提交塞多个无关改动 | 拆成多条 |
|
|
96
|
+
| subject 写"怎么做"而非"做了什么" | "用 useReducer 替换"而非"改了 state 逻辑" |
|
|
97
|
+
| 只说现象不说动机 | body 补上为什么 |
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill_name": "commit-message",
|
|
3
|
+
"evals": [
|
|
4
|
+
{
|
|
5
|
+
"id": 1,
|
|
6
|
+
"prompt": "我把一个 React 组件的状态从 useState 换成了 useReducer,还顺手修了个内存泄漏 bug。写个 commit message。",
|
|
7
|
+
"expected": "归类为 refactor,subject 说做了什么(不写怎么实现),body 解释为什么,提及附加的 bug 修复"
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"id": 2,
|
|
11
|
+
"prompt": "上一条提交信息是 'update',太笼统了。假设当前 diff 是给登录接口加了密码哈希校验,帮我重写成规范的。",
|
|
12
|
+
"expected": "重写为 feat/fix 类,scope=auth,subject 具体,符合 Conventional Commits"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"id": 3,
|
|
16
|
+
"prompt": "我改了登录接口、数据库迁移脚本、README 三个地方,怎么提交?",
|
|
17
|
+
"expected": "识别这是不相关改动,建议拆成 3 条提交而非塞一条"
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: debugging
|
|
3
|
+
description: 遇到 bug、报错、崩溃或行为异常,需定位根因时。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Debugging
|
|
7
|
+
|
|
8
|
+
## 概述
|
|
9
|
+
|
|
10
|
+
用科学方法定位 bug 的根因,而不是凭直觉乱改碰运气。核心:调试是**观察 → 假设 → 设计实验验证 → 缩小范围 → 定位根因**的循环,每一步都有依据。改到"不报错"不算修好——那可能只是把症状盖住了,根因还在。
|
|
11
|
+
|
|
12
|
+
## 何时使用
|
|
13
|
+
|
|
14
|
+
- 遇到报错/崩溃/行为异常,要定位原因
|
|
15
|
+
- bug 不稳定复现,不知触发条件
|
|
16
|
+
- 调试卡住、改了好几处都不行
|
|
17
|
+
- 看了代码"没发现问题",但程序确实有问题
|
|
18
|
+
|
|
19
|
+
**不该用**:行为符合预期的"不是 bug"(先确认是不是 bug,别把功能当 bug 调);环境/部署问题(先排查是不是代码外原因)。
|
|
20
|
+
|
|
21
|
+
**与相邻 skill 的边界**:`debugging` 管**定位根因**(调查:复现、假设、二分、读堆栈),`verify-and-fix` 管**修复与验证**(改对:修病因不症状、防回归)。两者接力——debugging 找出"问题是什么、在哪",verify-and-fix 接手"怎么改对、怎么确认修好"。debugging 的终点(定位的根因)就是 verify-and-fix 的起点。
|
|
22
|
+
|
|
23
|
+
**当 bug 报告太模糊时**("页面打不开""功能不工作"却没给报错/复现步骤),先回到 `clarifying-questions` 的思路——要可观察的现象(报错信息、触发操作、环境、是所有情况还是特定情况)再分析。没有现象的调试就是盲猜。
|
|
24
|
+
|
|
25
|
+
## 核心内容
|
|
26
|
+
|
|
27
|
+
### 第一原则:先看报错,别跳过它直接猜
|
|
28
|
+
|
|
29
|
+
报错信息(异常类型、消息、堆栈、行号)往往**直接包含根因线索**,是调试最便宜的情报。最常见、最浪费的错法是**不看报错就凭直觉改**——明明堆栈第 3 行写着 `Cannot read 'id' of undefined at line 42`,却跳过它去猜"是不是网络问题""是不是缓存"。
|
|
30
|
+
|
|
31
|
+
读报错的顺序:
|
|
32
|
+
|
|
33
|
+
1. **异常类型 + 消息**:发生了什么(TypeError?NullPointer?超时?)
|
|
34
|
+
2. **第一个你自己代码的堆栈帧**:在哪发生的(行号 + 函数)——注意是"你的代码",不是框架/库内部的帧
|
|
35
|
+
3. **触发上下文**:什么操作/数据触发的
|
|
36
|
+
|
|
37
|
+
**读堆栈的技巧**:堆栈常被框架/异步包装得很难读,几个技巧帮你找到真正的根因帧:
|
|
38
|
+
|
|
39
|
+
- **跳过框架帧**:堆栈顶部往往是一堆框架内部代码(React 调度、Express 中间件、ORM 反射),真正的根因在**第一个属于你项目源码的帧**——往下翻找到你认识的文件名/行号。
|
|
40
|
+
- **异步代码的堆栈可能不连续**:`async/await`、Promise、回调、事件循环的堆栈经常断开(一个错误在 setTimeout 里抛,堆栈却看不到触发它的代码)。现代运行时有 `--async-stack-traces` 或类似的异步堆栈支持,开启它;否则要在触发处手动打日志补全调用链。
|
|
41
|
+
- **错误被转发后原始堆栈会丢**:如果错误被 catch 又重新抛(尤其改了消息或包了新异常),原始堆栈可能藏在 `error.cause` 或 `originalError` 里——别只看最外层,挖嵌套的 cause 链。
|
|
42
|
+
|
|
43
|
+
养成习惯:**遇到 bug,第一件事是完整读一遍报错**,而不是打开编辑器开始改。读不懂报错时,先查懂它(搜异常类型、读文档),别跳过。
|
|
44
|
+
|
|
45
|
+
### 先复现,再调试
|
|
46
|
+
|
|
47
|
+
不能稳定复现的 bug 几乎无法调试——你改了不知道有没有效,因为"不报错"可能是修好了,也可能是这次没触发。**调试前先建立可复现**:
|
|
48
|
+
|
|
49
|
+
- 找到触发 bug 的**最小条件**:什么输入、什么操作顺序、什么状态组合下必现?
|
|
50
|
+
- 最小化:剥离无关因素,直到只剩"做 X 就必崩"。最小复现让你能反复试验、验证修复。
|
|
51
|
+
|
|
52
|
+
不稳定复现的 bug(偶发)尤其要先攻克复现——它通常意味着有**隐含条件**没找到(并发时序、特定数据、资源竞争、时间相关)。找这个条件本身就是定位根因的关键。
|
|
53
|
+
|
|
54
|
+
> 反例:bug 偶发,你直接多加几个 try/catch 把可能出错的地方包起来"这样就不崩了"——错误被吞了看不见,但触发条件和根因一行没动,换个场景又炸,而且现在连报错都没了,更难查。
|
|
55
|
+
|
|
56
|
+
### 科学方法:假设 → 实验 → 验证
|
|
57
|
+
|
|
58
|
+
定位根因靠**假设驱动**,不是碰运气:
|
|
59
|
+
|
|
60
|
+
1. **观察**:报错是什么、何时发生、复现条件。
|
|
61
|
+
2. **假设**:"我猜根因是 X"——基于观察和代码理解提出**具体、可证伪**的假设(不是"大概是哪里有问题")。
|
|
62
|
+
3. **设计实验**:如果是 X 导致的,那应该观察到 Y(可验证的预测)。
|
|
63
|
+
4. **验证**:跑实验,看 Y 是否成立。成立 → 假设支持,继续深入;不成立 → 排除这个假设,换下一个。
|
|
64
|
+
5. **缩小范围**:每次实验排除一部分可能性,把根因锁定在更小的范围。
|
|
65
|
+
|
|
66
|
+
关键纪律:**一次只验证一个假设**。同时改多个变量,有效了也不知道是哪个起作用——这是 shotgun debugging(散弹枪式乱改)的典型。
|
|
67
|
+
|
|
68
|
+
**什么样的假设是好假设**:调试假设要**具体且可证伪**——能说"如果是 X 导致的,那应该观察到 Y",并且能设计实验验证 Y。
|
|
69
|
+
|
|
70
|
+
- 差的假设:"大概是缓存的问题""可能是哪里有 bug"——模糊、无法验证,等于没假设,只会让你乱试。
|
|
71
|
+
- 好的假设:"我猜是用户列表为空时 find 返回 undefined 导致后续 .name 抛错——如果是,那传一个空数组应该必现,传非空不报。" 具体(空数组触发)、可证伪(传非空应该不报)、能设计实验(构造两种输入对比)。
|
|
72
|
+
|
|
73
|
+
提不出具体假设,说明你对问题观察得不够——回到读报错、看运行时真实数据、建立复现,先把现象搞清楚。
|
|
74
|
+
|
|
75
|
+
### 二分法:范围太大时快速缩小
|
|
76
|
+
|
|
77
|
+
当代码量大、不知问题在哪时,用**二分法**(git bisect 是经典实现):
|
|
78
|
+
|
|
79
|
+
- 注释/禁用一半代码,看 bug 还在不在
|
|
80
|
+
- 还在 → bug 在剩下的一半里;消失了 → bug 在被禁用的那一半里
|
|
81
|
+
- 对剩下一半重复,每轮砍一半
|
|
82
|
+
|
|
83
|
+
二分法把"在 10000 行里找 bug"变成"log₂(10000) ≈ 13 次实验"。它不依赖理解代码,纯靠排除——当你读代码读不出问题时,二分是最有力的工具。
|
|
84
|
+
|
|
85
|
+
适用场景:回归 bug(之前好的现在坏了,用 git bisect 定位是哪次提交引入的)、不知问题模块的大型代码库、读代码陷入僵局。
|
|
86
|
+
|
|
87
|
+
### 回归 bug:用好"曾经正常"这个参照点
|
|
88
|
+
|
|
89
|
+
"之前好的,现在坏了"是最容易调的一类 bug——你有一个**已知正常的版本**作参照,省掉了"重建预期行为"的功夫。策略:
|
|
90
|
+
|
|
91
|
+
- **git bisect 是首选**:在"好的"和"坏的"提交之间二分,每轮 git 自动 checkout 中间提交让你测,log₂(N) 次就能锁定引入 bug 的那次提交。这比读 diff 猜快得多。
|
|
92
|
+
- **锁定提交后看 diff**:那次提交改了什么,根因大概率就在 diff 里——范围从"整个代码库"缩到"一次提交的几处改动"。
|
|
93
|
+
- **想清楚"为什么之前没事"**:很多回归 bug 的根因是"旧代码依赖了一个隐含假设,新改动破坏了它"。问"这个改动为什么会引发这个症状"往往直接指向根因,比泛泛调试快。
|
|
94
|
+
|
|
95
|
+
### 区分"代码问题"和"运行时问题"
|
|
96
|
+
|
|
97
|
+
"看了代码没发现问题"不等于"代码没问题"——也可能是**运行时问题**,代码逻辑没错,但运行时的数据/环境/状态让行为出错:
|
|
98
|
+
|
|
99
|
+
- **代码问题**:逻辑写错(看代码能发现)。
|
|
100
|
+
- **运行时问题**:数据不符合预期(null、空、格式错)、环境差异(依赖版本、配置、权限)、状态问题(时序、并发、缓存残留)、外部依赖故障(网络、第三方服务)。
|
|
101
|
+
|
|
102
|
+
调试时两者都要查。如果代码读起来"没问题",转向查运行时:**实际数据是什么样?打印/日志看一下真实值**,而不是盯着代码猜。很多 bug 是"代码假设数据是 A,实际数据是 B"——光看代码发现不了,看真实数据立刻暴露。
|
|
103
|
+
|
|
104
|
+
## 常见错误
|
|
105
|
+
|
|
106
|
+
| 问题 | 修法 |
|
|
107
|
+
|------|------|
|
|
108
|
+
| 不看报错就凭直觉猜 | 第一件事完整读报错(类型/消息/堆栈/行号) |
|
|
109
|
+
| 用 try/catch 把错误盖住当"修好" | 那是掩盖症状,先定位根因(见 verify-and-fix) |
|
|
110
|
+
| 乱改试错(同时改多处) | 一次只改一个变量、验证一个假设 |
|
|
111
|
+
| 不能复现就硬调 | 先建立最小复现,否则改了也不知道有没有效 |
|
|
112
|
+
| 改到"不报错"就停 | 不报错≠修好,可能只是症状被盖住,查清根因 |
|
|
113
|
+
| 只读代码不看运行时数据 | 代码没问题可能是运行时问题,打印/日志看真实值 |
|
|
114
|
+
| 陷入循环(A↔B),继续硬改 | 停,回到稳定状态,换方法(二分/重新假设) |
|
|
115
|
+
| 范围太大读代码读不出 | 用二分法快速缩小,不依赖理解代码 |
|
|
116
|
+
| 回归 bug 逐行读 diff 猜 | 用 git bisect 在好/坏提交间二分,锁定引入提交再看那一次的 diff |
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill_name": "debugging",
|
|
3
|
+
"evals": [
|
|
4
|
+
{
|
|
5
|
+
"id": 1,
|
|
6
|
+
"prompt": "我的程序偶尔会崩,报个 TypeError,但是不稳定复现。我打算多加几个 try/catch 把可能出错的地方都包起来,这样就不会崩了。这个思路对吗?",
|
|
7
|
+
"expected": "不附和'加 try/catch 就不崩',识别为掩盖症状而非定位根因。指出偶发说明隐含条件没找到(并发/时序/特定数据),找条件即定位根因关键。建议先建立最小复现而非用 try/catch 盖住。区分 debugging(定位根因) vs 错误处理(已知错误怎么兜)"
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"id": 2,
|
|
11
|
+
"prompt": "用户反馈'页面打不开',我看了下代码没发现明显问题。你能帮我看看是哪里的 bug 吗?",
|
|
12
|
+
"expected": "不盲读代码猜,先要可观察现象(报错/复现步骤/环境)。反问关键信息(控制台/网络/服务端报错、触发操作、浏览器环境、所有页面还是特定页面)。指出'看了代码没发现问题'≠'代码没问题'——可能是运行时问题(网络/数据/环境/状态)。模糊 bug 报告先提具体可证伪假设再调查"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"id": 3,
|
|
16
|
+
"prompt": "这个 bug 我改了好几处都不行,改 A 报 B 错,改 B 又回到 A 错。我都改了一下午了。怎么办?",
|
|
17
|
+
"expected": "识别为 shotgun debugging + 原地打转(没假设乱改、A↔B 循环)。建议停下来回到稳定状态(撤销所有改动)而非继续硬改。科学方法重新调查(每次只改一个变量、验证一个假设、记录每步)。二分法缩小范围。调试是科学方法非碰运气。好假设要具体可证伪"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"id": 4,
|
|
21
|
+
"prompt": "我的 async 函数报错了,堆栈里全是 Promise.then 和 node_modules 里的东西,根本看不到我自己代码在哪一行。我该怎么定位?",
|
|
22
|
+
"expected": "指出堆栈顶部一堆框架/node_modules 帧要跳过,根因在第一个属于用户自己源码的帧。识别异步堆栈不连续(async/await/Promise 堆栈断开看不到触发代码),建议开启异步堆栈支持(--async-stack-traces)或手动打日志补全。提到错误被转发后原始堆栈丢,挖 error.cause/originalError 嵌套链。先完整读报错而非盲改"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"id": 5,
|
|
26
|
+
"prompt": "这个功能上个月还是好的,最近突然不行了。我打算把最近一个月改的代码都过一遍找问题。这样行吗?",
|
|
27
|
+
"expected": "识别为回归 bug,有比逐行读 diff 更高效的方法。建议 git bisect(好/坏提交间二分,log₂(N) 次锁定引入提交)而非逐行读一个月 diff。锁定提交后看那一次的 diff,根因大概率在 diff 里(范围从整个代码库缩到一次提交)。建议想清楚'为什么之前没事'(旧代码依赖隐含假设、新改动破坏了它)"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"id": 6,
|
|
31
|
+
"prompt": "有个 bug 只在生产环境偶发,本地怎么都复现不了。我加了好多 console.log 想抓现场,但加了一周也没碰到。怎么办?",
|
|
32
|
+
"expected": "识别'本地复现不了'是关键障碍——不能复现就无法验证修复有效。偶发意味着隐含条件没找到(生产特有:数据量/并发/特定用户数据/资源压力),找条件即定位根因关键。建议先建立可复现——模拟生产条件(相同数据量/并发/环境)逼近复现,而非纯靠 console.log 碰运气。区分 console.log 是观察手段而非定位手段——光打日志不提假设仍是盲等"
|
|
33
|
+
}
|
|
34
|
+
],
|
|
35
|
+
"_validation_history": {
|
|
36
|
+
"round_1": "v1 跑 case 1/2/3:3×4-5=13 项全过。但发现 v1 缺'读堆栈技巧''回归 bug 策略'两块",
|
|
37
|
+
"round_2": "v2 跑变体 case 4/5/6:3×4=12 项全过,压测两处新增能力(读堆栈/回归bug)全部生效,case6 回归复现能力保持",
|
|
38
|
+
"round_3": "v3 补'好假设要具体可证伪'(发现 v2 该项靠 subagent 凭经验、缺统一框架);回归 case 1/2/3:3×4-5=14 项全过(case3 首次执行失败重跑通过),确认 v3 生效且未破坏 v1/v2。迭代收敛"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: error-handling
|
|
3
|
+
description: 设计或改进错误处理与日志时。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Error Handling
|
|
7
|
+
|
|
8
|
+
## 概述
|
|
9
|
+
|
|
10
|
+
错误要**可观测**(知道发生了什么)、**可区分**(能区分类型)、**不吞**(不静默 catch)。好的错误处理让你在出问题时能快速定位,而不是对着空白或 `undefined` 发呆。
|
|
11
|
+
|
|
12
|
+
## 何时使用
|
|
13
|
+
|
|
14
|
+
- 写代码遇到边界/失败场景(空值、超时、外部调用失败),不知怎么处理
|
|
15
|
+
- 现有错误处理粗糙(裸 catch、吞异常),要改进
|
|
16
|
+
- 设计错误类型/错误码体系
|
|
17
|
+
- 决定"这个错该抛还是该处理、要不要重试"
|
|
18
|
+
|
|
19
|
+
**不该用**:确定性逻辑(没失败可能,别过度防御)。
|
|
20
|
+
|
|
21
|
+
## 核心内容
|
|
22
|
+
|
|
23
|
+
### 三态决策:抛 / 处理 / 重试
|
|
24
|
+
|
|
25
|
+
遇到一个可能的错误,先判断属于哪种:
|
|
26
|
+
|
|
27
|
+
- **抛(throw / propagate)**:你处理不了,或属于底层职责。往上传,让上层决策。例:数据库连不上,service 层抛,让调用方决定降级还是报错。
|
|
28
|
+
- **处理(handle)**:你能**在原地给出可接受的后续**——三种形态:①**恢复**(用默认值/缓存顶上,如配置缺失用默认配置);②**降级**(核心功能照跑、非核心跳过,如推荐服务挂了就返回空列表而非整页崩溃);③**转成业务语义**(把底层异常翻译成业务错误类型抛出,让边界层转成给用户的提示)。**业务错误(余额不足、邮箱已注册)归这里**——它们不是故障而是预期分支,不重试(结果不会变),抛一个业务错误类型即可。判断"能不能处理"的尺子:**处理后程序能否继续负责任地往下走?**能 → 处理;只是把错藏起来让上层踩坑 → 抛。
|
|
29
|
+
- **重试(retry)**:只对**瞬时**故障重试——网络抖动、连接超时、HTTP 429/503。指数退避、有上限,耗尽后归为"抛"。三个前提:① 有副作用的调用(支付、下单、写库)重试前必须保证幂等(幂等键或唯一约束),否则重试=重复扣款/重复写入;② **读超时**(请求已发出、响应没回来)结果可能已生效,按副作用对待,必须幂等;③ HTTP 429/503 优先遵守响应头 `Retry-After`。注意 4xx(非 429)和多数 5xx 不是瞬时错误,重试无意义,直接抛。
|
|
30
|
+
|
|
31
|
+
**原则**:越靠近错误的层越了解错误含义;但只有能负责任的层才该处理。不知道怎么办就抛上去。
|
|
32
|
+
|
|
33
|
+
### 日志
|
|
34
|
+
|
|
35
|
+
- **记什么**:上下文——输入、相关状态、错误对象(含堆栈)
|
|
36
|
+
- **敏感信息要脱敏后记,不要完全不记**:密码、token、身份证、银行卡——直接丢弃会丢失排查线索("是不是这个用户的请求出问题?")。正确做法是记**可识别但不可还原**的形式:token 记前 8 位 + `***`、手机号记后 4 位、密码只记"是否为空"不记内容。原则:能定位到"是哪个对象/哪次请求",但不能还原出敏感值本身。
|
|
37
|
+
- **分级**:`debug`(排查细节)、`info`(关键业务节点)、`warn`(可疑但可继续)、`error`(出错了需关注)
|
|
38
|
+
|
|
39
|
+
**业务错误 vs 系统错误的日志级别**:邮箱已注册、余额不足这类是**预期的业务拒绝**,不算系统故障——打 `info`/`warn` 即可,别打 `error` 污染告警;DB 连不上、网络失败这类**意外故障**才打 `error`。
|
|
40
|
+
|
|
41
|
+
错误日志要让你光看日志就重现问题现场,而不是只看到 `Error: something went wrong`。
|
|
42
|
+
|
|
43
|
+
### 错误信息面向开发者
|
|
44
|
+
|
|
45
|
+
错误消息给**排查的开发者**看,不是给终端用户看:写清**发生了什么** + **可能原因** + **怎么排查**,并带上下文。
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
// 差
|
|
49
|
+
throw new Error("failed")
|
|
50
|
+
|
|
51
|
+
// 好
|
|
52
|
+
throw new Error(`用户注册失败:邮箱 ${email} 已被注册。检查是否应走登录流程。`)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
给用户的提示要另外做(友好、不泄露技术细节),别直接把异常 message 吐给用户。
|
|
56
|
+
|
|
57
|
+
### 模式
|
|
58
|
+
|
|
59
|
+
- **自定义错误类型/错误码**:区分业务错误(邮箱已注册)和系统错误(DB 连不上),让上层针对性处理
|
|
60
|
+
- **fail fast**:启动期检查前置条件,不满足直接报错,别带病运行
|
|
61
|
+
- **边界统一兜底**:在系统边界(HTTP 中间件、API 网关)统一捕获并转换错误,别让内部异常直接漏到外部
|
|
62
|
+
- **别 catch 了又原样 throw**:要么加上下文再抛,要么处理掉
|
|
63
|
+
|
|
64
|
+
## 常见错误
|
|
65
|
+
|
|
66
|
+
| 问题 | 修法 |
|
|
67
|
+
|------|------|
|
|
68
|
+
| 裸 `catch(e){}` 静默吞错 | 至少记日志 + 决定抛/处理 |
|
|
69
|
+
| catch 了又 `throw e`(无添加) | 加上下文再抛,或处理掉 |
|
|
70
|
+
| 日志只记 `e.message` | 记上下文 + 完整错误对象 |
|
|
71
|
+
| 敏感信息要么全记要么全丢 | 脱敏后记(token 记前 8 位 + `***`),保留定位能力又不泄露 |
|
|
72
|
+
| 把堆栈直接吐给终端用户 | 内部日志详细,给用户友好提示 |
|
|
73
|
+
| 不区分错误类型 | 用自定义错误类型/错误码 |
|
|
74
|
+
| 无限重试/无退避 | 指数退避 + 最大次数 |
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill_name": "error-handling",
|
|
3
|
+
"evals": [
|
|
4
|
+
{
|
|
5
|
+
"id": 1,
|
|
6
|
+
"prompt": "这段代码 try { const r = await fetch(url); return r.json(); } catch(e) { console.log(e); } 太糙了,怎么改进?",
|
|
7
|
+
"expected": "指出裸 catch 静默吞错、日志缺上下文;按三态决策(网络瞬时→可重试),改进为分级日志+上下文+针对性处理"
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"id": 2,
|
|
11
|
+
"prompt": "我要调用一个第三方支付 API,可能超时、被限流(429)、余额不足。这些错误分别该怎么处理?",
|
|
12
|
+
"expected": "超时/限流→瞬时故障,指数退避重试(支付须幂等);余额不足→业务错误不重试,抛业务错误类型给上层;能区分'恢复/降级/转业务语义'三种处理形态,而非笼统说'catch 处理'"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"id": 3,
|
|
16
|
+
"prompt": "写一个用户注册函数:邮箱格式错、邮箱已注册、数据库连不上。这三种错误怎么设计错误类型和返回?",
|
|
17
|
+
"expected": "自定义错误类型区分业务错误(格式/已注册)和系统错误(DB),错误信息面向开发者含上下文,给用户的提示单独做"
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: lion-writing-skills
|
|
3
|
+
description: 在 Lion-Skills 仓库创建或修改 skill 时。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Lion Writing Skills
|
|
7
|
+
|
|
8
|
+
## 概述
|
|
9
|
+
|
|
10
|
+
Lion-Skills 的"造 skill 流水线":按本仓库规范,把一个重复的工作流沉淀成可复制、经验证的 skill。
|
|
11
|
+
|
|
12
|
+
核心原则:skill 是给未来 Claude 读的"操作手册"——它的价值不在于你写了什么,而在于未来的 Claude 在触发它时,能照着做出正确的事。
|
|
13
|
+
|
|
14
|
+
## 何时使用
|
|
15
|
+
|
|
16
|
+
- 用户想新增一个 skill("帮我加个 X 的 skill")
|
|
17
|
+
- 用户想修改/优化现有 skill
|
|
18
|
+
- 用户想检查某个 skill 写得是否符合规范
|
|
19
|
+
- 想把对话里反复出现的工作流沉淀成 skill
|
|
20
|
+
|
|
21
|
+
**不该做成 skill 的情况**:一次性的、项目特定的约定(放 CLAUDE.md)、能用简单校验自动化的(写成校验脚本而不是文档)。
|
|
22
|
+
|
|
23
|
+
**只是审查/优化现有 skill 时**:直接对照下文相关规则做合规判断即可,不必每次重走第 1 步"值不值得做"四问——那是**新增** skill 的决策;现有 skill 要不要继续存在,是另一个问题。
|
|
24
|
+
|
|
25
|
+
## 核心内容
|
|
26
|
+
|
|
27
|
+
### 第 1 步:判断值不值得做
|
|
28
|
+
|
|
29
|
+
回答四个问题(都"是"才值得做):
|
|
30
|
+
1. 高频吗?(反复出现,不是一次性的)
|
|
31
|
+
2. 跨项目吗?(不止一个代码库用得到)
|
|
32
|
+
3. 有判断空间吗?(需要决策/经验,不是机械操作——机械操作写成脚本)
|
|
33
|
+
4. 未来 Claude 不读这个 skill,会做错或做差吗?
|
|
34
|
+
|
|
35
|
+
任何一个答"否",就别做成独立 skill:项目特定的约定放 CLAUDE.md,能机械自动化的写成校验脚本,一次性的直接在对话里处理。
|
|
36
|
+
|
|
37
|
+
### 第 2 步:复制模板,填 frontmatter
|
|
38
|
+
|
|
39
|
+
新 skill 建在 `skills/<name>/SKILL.md`(同名目录,里面放 `SKILL.md`,可选再放 `evals/` 存验证用例)。复制 `template/SKILL.md` 过去,填两个字段:
|
|
40
|
+
- `name`:全小写、连字符,用 skill 的核心动作或概念命名(如 `commit-message`、`error-handling`、`onboarding-unknown-codebase`)。不要下划线或驼峰(`commit_message`、`CommitMessage` 都错)
|
|
41
|
+
- `description`:**只写"何时触发"**,第三人称,中文,用三段式——**①触发场景**("当用户……时使用")+ **②典型信号**(把用户实际会说的原话尽量列全)+ **③涉及关键词**(中文说法 + 常见英文术语)。这样覆盖面广、不易漏触发。上限 1024 字符
|
|
42
|
+
|
|
43
|
+
> 关键:description 绝不能写工作流("先做 A 再做 B")。原因——测试发现 Claude 会偷懒只读 description 跳过正文。把流程留给正文。
|
|
44
|
+
|
|
45
|
+
### 第 3 步:写正文
|
|
46
|
+
|
|
47
|
+
按这个骨架:概述 / 何时使用 / 核心内容 / 常见错误(参考 `template/SKILL.md`)。
|
|
48
|
+
|
|
49
|
+
写作约定:
|
|
50
|
+
- **解释 why**,少用全大写 MUST(讲道理比下命令管用)
|
|
51
|
+
- **一个优秀例子** > 多语言版本;例子尽量覆盖该 skill 最核心的 2–3 类场景,而非只示范单类
|
|
52
|
+
- 正文 < 500 行;超了拆 `references/`
|
|
53
|
+
- 中文为主,工具名/术语保留英文
|
|
54
|
+
|
|
55
|
+
### 第 4 步:轻量验证
|
|
56
|
+
|
|
57
|
+
**前置**:先确认目标 SKILL.md 在手(文件路径或正文)。用户只说"帮我验证"却没给内容时,先向用户要;skill 还在草稿态没落盘时,把全文直接贴进 subagent 的 prompt 即可,效果等同落盘。
|
|
58
|
+
|
|
59
|
+
写 2–3 个真实测试 prompt,用 subagent 带着新 skill 跑,看产出对不对。**这一步最容易被跳过,但也最能发现问题,别省。**
|
|
60
|
+
|
|
61
|
+
**怎么算"真实" prompt**:要像真实用户会说的、带具体细节——好的如"我把组件状态从 useState 换成 useReducer,还修了个内存泄漏,写个 commit message";坏的如"测一下你的能力"(太抽象,既测不出触发、也测不出指引力)。
|
|
62
|
+
|
|
63
|
+
**范式**:派一个 general-purpose subagent(让它只读 skill、把产出放进报告,不要改动仓库——除非任务就是要它落地文件),prompt 写「读取 `skills/<name>/SKILL.md`,然后按其指引处理这个需求:<测试 prompt>。完成后报告:你走了哪些步骤、产出什么、哪里卡壳」。多个 prompt 可以并行派。
|
|
64
|
+
|
|
65
|
+
**通过标准**:subagent 的产出 (a) 解决了用户问题,且 (b) 遵循 skill 的设计意图。把该 skill 正文里"该走的流程""该遵循的约定"抽成几条可勾选项来核(如 description 没写工作流、正文解释了 why、例子覆盖核心场景)。**跑偏长这样**:该走五步的跳过了验证、该三段式的 description 写成了能力清单、该按维度系统的退化成逐条枚举——出现这些就改正文重跑。
|
|
66
|
+
|
|
67
|
+
**把用例固化下来**:把这几个 prompt + 预期结果写进 `skills/<name>/evals/evals.json`(格式:`{skill_name, evals: [{id, prompt, expected}]}`)。evals.json 是验证的"存档"(记录测过什么、预期什么),subagent 实跑是"当下执行"。两者配合时注意时序:prompt 可以先定,但 `expected` 往往要跑一轮、看到实际产出才写得准——所以实操是「粗定 prompt → 跑 → 用实际产出校准 expected → 固化」,而不是单向的"先写完 evals.json 再跑"。
|
|
68
|
+
|
|
69
|
+
### 第 5 步:迭代
|
|
70
|
+
|
|
71
|
+
输出不对就改正文,重跑。重点改:
|
|
72
|
+
- 触发不准 → 改 description(补关键词、加症状)
|
|
73
|
+
- 做错了 → 正文讲清 why 或加常见错误
|
|
74
|
+
- 啰嗦/跑偏 → 删冗余
|
|
75
|
+
- **正文流程漏了关键步骤 → 补步骤**(这种问题几乎只能靠验证暴露,是迭代里最高频也最有价值的产出)
|
|
76
|
+
|
|
77
|
+
## 常见错误
|
|
78
|
+
|
|
79
|
+
| 问题 | 修法 |
|
|
80
|
+
|------|------|
|
|
81
|
+
| description 写了工作流(→ Claude 只读 description 跳过正文) | description 只留触发条件 |
|
|
82
|
+
| 正文超 500 行没拆(→ 加载慢、难维护) | 大段参考拆到 `references/` |
|
|
83
|
+
| 堆 MUST 不讲 why(→ 死板、难泛化) | 解释原因 |
|
|
84
|
+
| 一个例子写多语言(→ 平庸、难维护) | 一个优秀例子 |
|
|
85
|
+
| name 用下划线/驼峰 | 全小写连字符 |
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill_name": "lion-writing-skills",
|
|
3
|
+
"evals": [
|
|
4
|
+
{
|
|
5
|
+
"id": 1,
|
|
6
|
+
"prompt": "我想加个 skill,每次写完代码帮我自查有没有边界情况没处理。帮我按 Lion-Skills 的规范创建。",
|
|
7
|
+
"expected": "走五步流程,产出一个符合规范的 SKILL.md 草稿(含 frontmatter、正文骨架),而非直接堆内容"
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"id": 2,
|
|
11
|
+
"prompt": "我的 skill 的 description 是'用于代码审查,会检查风格、安全、性能',帮我看看写得对不对、怎么优化。",
|
|
12
|
+
"expected": "指出这违反'别写工作流'规则(写了会做什么),改成只写触发条件"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"id": 3,
|
|
16
|
+
"prompt": "我写了这个 SKILL.md,帮我验证它真好用。",
|
|
17
|
+
"expected": "引导用 subagent 带着新 skill 跑 2-3 个真实 prompt 并评审输出"
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
}
|