@chengzhao-dev/dsh-cpp-workflow 0.3.0 → 0.3.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.
Files changed (44) hide show
  1. package/README.en.md +7 -5
  2. package/README.md +7 -5
  3. package/lib/workspace.js +1 -56
  4. package/package.json +1 -1
  5. package/skills/building-cpp/SKILL.md +2 -2
  6. package/skills/building-cpp/assets/config/.clang-tidy +1 -0
  7. package/skills/building-cpp/assets/templates/projects/multi/CMakeLists.txt +2 -2
  8. package/skills/building-cpp/assets/templates/projects/shared-library/CMakeLists.txt +1 -1
  9. package/skills/building-cpp/assets/templates/projects/shared-library/greeting/CMakeLists.txt +1 -1
  10. package/skills/building-cpp/assets/templates/projects/single/CMakeLists.txt +1 -1
  11. package/skills/building-cpp/assets/templates/projects/static-library/CMakeLists.txt +1 -1
  12. package/skills/building-cpp/assets/templates/projects/static-library/greeting/CMakeLists.txt +1 -1
  13. package/skills/building-cpp/examples/cmake.md +15 -15
  14. package/skills/building-cpp/examples/tooling.md +3 -3
  15. package/skills/building-cpp/references/cmake.md +6 -6
  16. package/skills/building-cpp/references/linking.md +6 -6
  17. package/skills/building-cpp/references/static-analysis.md +3 -3
  18. package/skills/building-cpp/steps/01-build.md +1 -1
  19. package/skills/building-cpp/steps/02-tooling.md +2 -2
  20. package/skills/developing-cpp/SKILL.md +17 -3
  21. package/skills/developing-cpp/examples/comments.md +151 -138
  22. package/skills/developing-cpp/examples/structure.md +4 -4
  23. package/skills/developing-cpp/references/comments.md +152 -81
  24. package/skills/developing-cpp/references/declaration-order.md +4 -2
  25. package/skills/developing-cpp/references/file-template.md +131 -0
  26. package/skills/developing-cpp/references/glossary.md +3 -3
  27. package/skills/developing-cpp/references/google-cpp-style.md +8 -8
  28. package/skills/developing-cpp/references/language.md +3 -3
  29. package/skills/developing-cpp/references/layout.md +21 -12
  30. package/skills/developing-cpp/references/naming.md +3 -2
  31. package/skills/developing-cpp/references/project-design.md +1 -1
  32. package/skills/developing-cpp/references/standard-library.md +1 -1
  33. package/skills/developing-cpp/scripts/checking-comments.mjs +316 -0
  34. package/skills/developing-cpp/steps/01-behavior.md +1 -1
  35. package/skills/developing-cpp/steps/02-boundary.md +4 -4
  36. package/skills/developing-cpp/steps/03-declaration.md +5 -5
  37. package/skills/developing-cpp/steps/04-implementation.md +3 -3
  38. /package/skills/building-cpp/assets/templates/projects/multi/src/{greeting.cc → greeting.cpp} +0 -0
  39. /package/skills/building-cpp/assets/templates/projects/multi/src/{main.cc → main.cpp} +0 -0
  40. /package/skills/building-cpp/assets/templates/projects/shared-library/greeting/src/{greeting.cc → greeting.cpp} +0 -0
  41. /package/skills/building-cpp/assets/templates/projects/shared-library/src/{main.cc → main.cpp} +0 -0
  42. /package/skills/building-cpp/assets/templates/projects/single/src/{main.cc → main.cpp} +0 -0
  43. /package/skills/building-cpp/assets/templates/projects/static-library/greeting/src/{greeting.cc → greeting.cpp} +0 -0
  44. /package/skills/building-cpp/assets/templates/projects/static-library/src/{main.cc → main.cpp} +0 -0
@@ -5,258 +5,271 @@
5
5
  **差**:
6
6
 
7
7
  ```cpp
8
- // task_list.cc
9
- // 作者:张三
10
- // 创建:2024-01-01
11
- // 修改:2024-02-01 修复 bug
8
+ // board.h
9
+ // 作者:chengzhao-dev
10
+ // 创建:2026-01-01
11
+ // 修改:2026-03-02 增加 CompletesLine
12
12
  ```
13
13
 
14
14
  **好**:
15
15
 
16
16
  ```cpp
17
17
  //===----------------------------------------------------------------------===//
18
- // TaskList 的存储、优先级排序与稳定顺序。
18
+ // board.h - 定义井字棋棋盘。
19
19
  //
20
- // 不处理终端输入输出;程序层负责解析「优先级 名称」并打印。
20
+ // 棋盘只保存格子状态与规则判断,不含对局流程;对局流程在 game.h。
21
21
  //===----------------------------------------------------------------------===//
22
22
  ```
23
23
 
24
- 文件头回答「这个文件负责什么、不负责什么」,不写作者和变更史。
24
+ 文件头回答「这个文件负责什么、不负责什么」,不写作者和变更史。后两行是这条边界:读的人由此知道该去哪个文件找回合推进。
25
25
 
26
- ## 声明注释
26
+ ## 类型注释:概念与约定,不是功能清单
27
27
 
28
28
  **差**:
29
29
 
30
30
  ```cpp
31
- /// 获取任务。
32
- /// @param index 索引
33
- /// @return 任务
34
- Task GetTask(int index) const;
31
+ /// 井字棋棋盘。
32
+ ///
33
+ /// 提供检查坐标区间、检查棋盘格子是否为空、获取棋盘格子类型、放置棋子、清空棋盘和判定终局的功能。
34
+ /// 棋盘为 3 行 3 列。
35
+ class Board {
36
+ ```
37
+
38
+ 问题有两处:把成员函数抄了一遍,接口一改就过期;「棋盘为 3 行 3 列」被拆成单独一句,读者不知道它在回答什么问题。
39
+
40
+ **差**(把别的类型和以后才发生的事都写进来):
41
+
42
+ ```cpp
43
+ /// 井字棋棋盘。
44
+ ///
45
+ /// 棋盘只保存 3×3 的格子状态和规则判断,不含回合推进:谁该落子、落子被拒后怎么提示留在 Game。
46
+ ///
47
+ /// 棋盘是值语义,可以自由拷贝;所有查询都是 const,对局和搜索可以交替读取同一棋盘。
48
+ class Board {
35
49
  ```
36
50
 
37
- 问题:没说越界行为,没说返回的是拷贝还是引用。
51
+ 第二段说的是 `Game` 负责什么,读者读完仍不知道 `Board` 保证什么,重构一次这句话就过期;后两段里的可拷贝、`const` 查询在类声明里已经写清,「搜索要读同一棋盘」还不是现在的调用方。读者真正需要的是这个类型自己的不变量与失败方式。
38
52
 
39
53
  **好**:
40
54
 
41
55
  ```cpp
42
- /// 返回 index 处的任务副本。
56
+ /// 井字棋棋盘。
43
57
  ///
44
- /// @param index 任务下标,必须在 [0, tasks_.size()) 内
45
- /// @return 该位置的任务
46
- /// @throws TaskIndexError index 越界
47
- [[nodiscard]] Task GetTask(int index) const;
58
+ /// 3×3 个格子,每格取空、X 或 O;越界坐标不会写入棋盘。
59
+ class Board {
48
60
  ```
49
61
 
50
- 契约完整:范围、返回、异常声明。
62
+ 两句都是本类型能兑现、读代码又不容易一眼看全的事,函数各自的契约下沉到函数注释;设计理由与分层不写进注释,写进书稿。
51
63
 
52
- ## 实现注释
64
+ ## 风格理由不进注释
65
+
66
+ **差**(在十几个函数上各写一遍):
67
+
68
+ ```cpp
69
+ /// 检查坐标是否在范围内。函数名用 UpperCamel 而不是 snake_case,与标准库区分。
70
+ [[nodiscard]] static bool IsInRange(CellPosition position) noexcept;
71
+
72
+ /// 检查格子是否为空。函数名用 UpperCamel 而不是 snake_case,与标准库区分。
73
+ [[nodiscard]] bool IsCellEmpty(CellPosition position) const;
74
+ ```
75
+
76
+ **好**(只写契约):
77
+
78
+ ```cpp
79
+ /// 检查坐标是否落在棋盘下标区间内。
80
+ ///
81
+ /// 只判断几何边界,不检查格子是否为空、对局是否已经结束。
82
+ [[nodiscard]] static bool IsInRange(CellPosition position) noexcept;
83
+ ```
84
+
85
+ 命名与风格约定写进项目文档和读者正文,注释里不解释「为什么用这种命名」。
86
+
87
+ ## 函数契约
53
88
 
54
89
  **差**:
55
90
 
56
91
  ```cpp
57
- // 如果 tasks_ 为空
58
- if (tasks_.empty()) {
59
- // 返回空
60
- return {};
61
- }
62
- // 遍历 tasks_
63
- for (const auto& task : tasks_) {
64
- // 比较优先级
65
- if (task.priority < best.priority) {
66
- // 更新 best
67
- best = task;
68
- }
69
- }
92
+ /// 尝试执行一次落子。
93
+ ///
94
+ /// @param move 落子。
95
+ /// @returns 落子执行成功返回 kPlaced,被拒绝返回 kRejected。
96
+ [[nodiscard]] PlaceResult TryPlace(const Move& move);
70
97
  ```
71
98
 
72
- 每行都在复述代码。
99
+ `@param move 落子` 是把类型名翻译成中文,没有信息;`@returns` 不是 Doxygen 标签;没有说被拒绝的条件,调用方只能读实现。
73
100
 
74
101
  **好**:
75
102
 
76
103
  ```cpp
77
- // 空列表是合法状态,调用方不应被迫先判空。
78
- if (tasks_.empty()) {
79
- return {};
80
- }
81
-
82
- // 优先级相同时保留先加入的任务:用严格小于,不交换等值元素。
83
- for (const auto& task : tasks_) {
84
- if (task.priority < best.priority) {
85
- best = task;
86
- }
87
- }
104
+ /// 尝试执行一次落子。
105
+ ///
106
+ /// 只有本局仍在进行、且落子玩家正是当前玩家时才继续,棋盘、轮次、历史三者一起更新。
107
+ ///
108
+ /// @param move 待执行的落子,玩家必须是当前轮到的玩家。
109
+ /// @return 落子写入棋盘并换手时返回 kPlaced;对局已结束或上述任一条件不满足时返回 kRejected,
110
+ /// 棋盘和轮次都不变。
111
+ [[nodiscard]] PlaceResult TryPlace(const Move& move);
88
112
  ```
89
113
 
90
- 解释「为什么」和「稳定排序的取舍」,不翻译语句。
91
-
92
- ## TODO
114
+ ## 布尔查询要写「不检查什么」
93
115
 
94
116
  **差**:
95
117
 
96
118
  ```cpp
97
- // TODO: 参考 xxx
98
- // FIXME: 这里可能有问题
99
- // TODO(张三): 优化
119
+ /// 检查指定坐标上的棋盘格子是否为空。
120
+ [[nodiscard]] bool IsCellEmpty(CellPosition position) const;
100
121
  ```
101
122
 
102
123
  **好**:
103
124
 
104
125
  ```cpp
105
- // TODO: 阶段 2 - 支持按截止日期排序
106
- // TODO: issue #42 - 把优先级解析抽成 TryParsePriority
126
+ /// 检查指定坐标上的棋盘格子是否为空。
127
+ ///
128
+ /// 只回答「这一格能不能落子」,不判断对局是否已经结束:对局结束后格子仍是空的,
129
+ /// 但再落子没有意义。是否需要先看对局状态由调用方决定,棋盘不替它下结论。
130
+ ///
131
+ /// @param position 待检查的坐标。
132
+ /// @return 坐标在棋盘内且该格为空时返回 true;越界或该格已有棋子时返回 false。
133
+ [[nodiscard]] bool IsCellEmpty(CellPosition position) const;
107
134
  ```
108
135
 
109
- 有定位、有动作、能搜索。
136
+ 布尔函数最容易被误用;把「不检查什么」写出来,比多写一个参数说明有用。
110
137
 
111
- ## 配置注释
138
+ ## 枚举
112
139
 
113
140
  **差**:
114
141
 
115
- ```yaml
116
- # 这是 clang-format 配置
117
- # 详见某文档
118
- ColumnLimit: 100
142
+ ```cpp
143
+ enum class Player : std::uint8_t {
144
+ kCross, // X
145
+ kNought // O
146
+ };
119
147
  ```
120
148
 
121
149
  **好**:
122
150
 
123
- ```yaml
124
- # 列宽、缩进、包含顺序都取 Google 预设,不逐项覆盖。
125
- Language: Cpp
126
- BasedOnStyle: Google
127
- Standard: Latest
151
+ ```cpp
152
+ /// 对局结果。
153
+ enum class GameResult : std::uint8_t {
154
+ kInProgress = 0, ///< 游戏进行中:还可以落子。
155
+ kCrossWin = 1, ///< X 获胜:达成三连。
156
+ kNoughtWin = 2, ///< O 获胜:达成三连。
157
+ kDraw = 3, ///< 平局:棋盘已满,双方都没有三连。
158
+ };
128
159
  ```
129
160
 
130
- 解释为什么选预设,不写策略头,不指文档路径。
161
+ 行尾注释先写语义,需要时用冒号补一句能核实的条件或结果,两段都短;不重复名字,不写嵌套子句,同一枚举要么全写要么全不写。
131
162
 
132
- ## 横幅
163
+ ## 数据成员
133
164
 
134
165
  **差**:
135
166
 
136
167
  ```cpp
137
- // ===== 函数 =====
138
- void AddTask();
139
- void RemoveTask();
140
- // ===== 结束 =====
168
+ Board board_;
169
+ Player current_player_;
170
+ GameResult result_;
141
171
  ```
142
172
 
143
173
  **好**:
144
174
 
145
175
  ```cpp
146
- //===----------------------------------------------------------------------===//
147
- // 查询
148
- //===----------------------------------------------------------------------===//
149
-
150
- [[nodiscard]] std::size_t Size() const;
151
- [[nodiscard]] bool IsEmpty() const;
176
+ Board board_; // 对局棋盘。
177
+ Player current_player_ = Player::kCross; // 当前轮到的玩家,开局时 X 先手。
178
+ GameResult result_ = GameResult::kInProgress; // 对局结果;落子被拒时不改写。
152
179
  ```
153
180
 
154
- 横幅是跳转锚点,不是装饰;一组函数一个横幅,单函数不加。
181
+ 行尾写「存什么 + 初值含义 + 什么时候变」,同类成员对齐。
155
182
 
156
- ## 枚举行尾注释
183
+ ## 实现注释
157
184
 
158
185
  **差**:
159
186
 
160
187
  ```cpp
161
- enum class TaskStatus : std::uint8_t {
162
- kPending, // 待处理
163
- kActive, // 处理中
164
- kBlocked, // 等待外部输入
165
- kDone // 已完成
166
- };
188
+ // 遍历所有空格
189
+ for (const auto& position : empty_cells) {
190
+ // 尝试落子
191
+ const bool wins = game.Board().CompletesLine(position, cell_type);
192
+ // 如果能赢
193
+ if (wins) {
194
+ // 返回这一手
195
+ return position;
196
+ }
197
+ }
167
198
  ```
168
199
 
169
- 问题:四个成员都写等于没写,且把名字重复了一遍。
200
+ 每行都在复述代码。
170
201
 
171
202
  **好**:
172
203
 
173
204
  ```cpp
174
- enum class TaskStatus : std::uint8_t {
175
- kPending, ///< 已登记,尚未开始
176
- kActive, ///< 正在处理
177
- kBlocked, ///< 等待外部输入,不能继续
178
- kDone ///< 已完成,不再改动
179
- };
205
+ // 先找能立刻赢的格子:启发式只在「马上赢」和「马上挡」之间做选择,
206
+ // 不在这里做更深的前瞻,深搜留给 MinimaxAi。
207
+ for (const auto& position : empty_cells) {
208
+ if (game.CompletesLine(position, CellType::kX)) {
209
+ return position;
210
+ }
211
+ }
180
212
  ```
181
213
 
182
- 若只有部分成员需要解释,同一枚举内要写就写全,或全不写;不要半套。语义结果写在最前,判定条件放冒号后。
214
+ 解释「为什么停在这一层」,不翻译语句。
183
215
 
184
- ## 声明注释的完整契约
216
+ ## TODO
185
217
 
186
218
  **差**:
187
219
 
188
220
  ```cpp
189
- /// 修改任务优先级。
190
- /// @param id 任务编号
191
- /// @param priority 优先级
192
- void SetPriority(TaskId id, int priority);
221
+ // TODO: 参考 xxx
222
+ // FIXME: 这里可能有问题
193
223
  ```
194
224
 
195
225
  **好**:
196
226
 
197
227
  ```cpp
198
- /// 修改任务优先级。
199
- ///
200
- /// @param id 任务编号,必须已存在
201
- /// @param priority 新优先级,必须在 [0, 9] 内
202
- /// @throws TaskIndexError 编号不在当前任务表内
203
- /// @throws InvalidPriorityError priority 越界
204
- void SetPriority(TaskId id, int priority);
228
+ // TODO: 阶段 5 - 给 MinimaxAi 加置换表,减少重复局面的搜索。
205
229
  ```
206
230
 
207
- 只写调用者无法从签名推出来的内容:范围、所有权、失败方式。`@brief` 不写,第一行就是摘要。
231
+ 有定位、有动作、能搜索。
208
232
 
209
- ## 相邻注释
233
+ ## 配置注释
210
234
 
211
235
  **差**:
212
236
 
213
- ```cpp
214
- // 这里先检查存在性
215
- // 然后检查状态
216
- // 最后写入
217
- if (!Contains(id)) return false;
218
- if (tasks_[Index(id)].status == TaskStatus::kDone) return false;
219
- tasks_[Index(id)].status = TaskStatus::kActive;
220
- return true;
237
+ ```yaml
238
+ # 这是 clang-format 配置
239
+ # 详见某文档
240
+ ColumnLimit: 100
221
241
  ```
222
242
 
223
243
  **好**:
224
244
 
225
- ```cpp
226
- // 先检查再写入:失败时不留下半完成状态。
227
- if (!Contains(id)) return false;
228
- if (tasks_[Index(id)].status == TaskStatus::kDone) return false;
229
- tasks_[Index(id)].status = TaskStatus::kActive;
230
- return true;
245
+ ```yaml
246
+ # 列宽、缩进、包含顺序都取 Google 预设,不逐项覆盖。
247
+ Language: Cpp
248
+ BasedOnStyle: Google
249
+ Standard: Latest
231
250
  ```
232
251
 
233
- 一段连续的实现注释只讲一个判断或一个取舍;不是逐行字幕。
252
+ 解释为什么选预设,不写策略头,不指文档路径。
234
253
 
235
- ## 成员还是同一命名空间的函数
254
+ ## 不该进注释的东西
236
255
 
237
256
  **差**:
238
257
 
239
258
  ```cpp
240
- class TaskTable {
241
- public:
242
- std::size_t CountOverdue() const;
243
- bool HasOverdue() const { return CountOverdue() > 0; }
244
- };
259
+ // 2026-03-02 把 Board 从 Game 里拆出来,因为之前 Game 太长。
260
+ // 参见 https://example.com/2026/03/02/refactor
261
+ // 其余成员见下方注释,两处不重复。
262
+ // 本团队要求所有类注释写满五行。
245
263
  ```
246
264
 
247
- `HasOverdue` 只组合公开查询,不需要直接访问表示,却做成了成员。
248
-
249
265
  **好**:
250
266
 
251
267
  ```cpp
252
- class TaskTable {
253
- public:
254
- std::size_t CountOverdue() const;
255
- };
256
-
257
- bool HasOverdue(const TaskTable& table) {
258
- return table.CountOverdue() > 0;
259
- }
268
+ //===----------------------------------------------------------------------===//
269
+ // board.h - 棋盘状态与规则判断
270
+ //
271
+ // 这一层只有格子状态和胜负判定;回合推进在 game.h,输入输出在 apps/。
272
+ //===----------------------------------------------------------------------===//
260
273
  ```
261
274
 
262
- 函数和类放在同一命名空间;调用方只看到 `HasOverdue(table)`,类不增加只为转发而存在的成员。
275
+ 变更史进提交说明,外部资料进文档,团队流程进规范文件,跨文件的边界写文件头;类注释只写本类型自己的契约。
@@ -20,7 +20,7 @@
20
20
  **差**:库函数直接打印,程序只调用它。
21
21
 
22
22
  ```cpp
23
- // task_list.cc(库)
23
+ // task_list.cpp(库)
24
24
  void TaskList::Print() const {
25
25
  for (const auto& task : tasks_) {
26
26
  std::cout << task.priority << " " << task.name << "\n";
@@ -36,7 +36,7 @@ void TaskList::Print() const {
36
36
  // task_list.h(库)
37
37
  [[nodiscard]] std::vector<Task> SortedByPriority() const;
38
38
 
39
- // app/cli.cc(程序)
39
+ // app/cli.cpp(程序)
40
40
  void PrintTasks(const std::vector<Task>& tasks, std::ostream& out) {
41
41
  for (const auto& task : tasks) {
42
42
  out << std::format("{} {}\n", task.priority, task.name);
@@ -63,7 +63,7 @@ src/detail.h // 私有头被测试直接包含
63
63
 
64
64
  ```text
65
65
  task_list/include/task_list.h // 公共头
66
- task_list/src/task_list.cc // 实现
66
+ task_list/src/task_list.cpp // 实现
67
67
  task_list/src/detail.h // 私有头,只被本组件包含
68
68
  ```
69
69
 
@@ -75,7 +75,7 @@ task_list/src/detail.h // 私有头,只被本组件包含
75
75
 
76
76
  ## 单文件何时拆库
77
77
 
78
- **先不拆**:一个 `main.cc`,辅助函数在匿名命名空间,只有程序自己用。
78
+ **先不拆**:一个 `main.cpp`,辅助函数在匿名命名空间,只有程序自己用。
79
79
 
80
80
  **该拆**:测试需要直接调用排序逻辑;或第二个程序要复用同一逻辑;或逻辑已经超过一个屏幕。
81
81