@chengzhao-dev/dsh-cpp-workflow 0.5.4 → 0.5.5

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/README.en.md CHANGED
@@ -19,13 +19,13 @@ The exception material follows the C++ Core Guidelines' Error handling section a
19
19
  Pinned npm install:
20
20
 
21
21
  ```bash
22
- dsh plugin --profile web add npm:@chengzhao-dev/dsh-cpp-workflow@0.5.4
22
+ dsh plugin --profile web add npm:@chengzhao-dev/dsh-cpp-workflow@0.5.5
23
23
  ```
24
24
 
25
25
  The matching Git tag:
26
26
 
27
27
  ```bash
28
- dsh plugin --profile web add github:chengzhao-dev/dsh-cpp-workflow#v0.5.4
28
+ dsh plugin --profile web add github:chengzhao-dev/dsh-cpp-workflow#v0.5.5
29
29
  ```
30
30
 
31
31
  The desktop app owns the `desktop` profile. If the command-line `add` is rejected, add the plugin in the app settings and use the same pinned npm version. A Git tag install uses the prebuilt `lib/` committed in the repository and runs no build on the target machine; the package has no install script that needs approval.
@@ -103,9 +103,11 @@ npm ci
103
103
  npm test # build, three-skill structural check, gate/environment/schema/comment tests
104
104
  npm run test:matrix # real build matrix: 4 templates x 5 standards
105
105
  npm run pack:check # inspect the npm package contents
106
+ npm run ci # replay the Ubuntu CI job: install, test, package check
107
+ npm run ci:full # ci plus the standard matrix; needs a POSIX host
106
108
  ```
107
109
 
108
- Source lives in `src/`; `npm run build` writes `lib/`. The structural check is `scripts/checking-skill-links.mjs`. Tests do not invoke a local compiler; the environment checks use fixed version strings. The standard matrix needs `cmake`, `ninja`, `perl`, and `clang++` (or the compiler named by `CXX`) on a POSIX host such as WSL, Linux, or macOS.
110
+ Source lives in `src/`; `npm run build` writes `lib/`. The structural check is `scripts/checking-skill-links.mjs`. Tests do not invoke a local compiler; the environment checks use fixed version strings. The standard matrix needs `cmake`, `ninja`, `perl`, and `clang++` (or the compiler named by `CXX`) on a POSIX host such as WSL, Linux, or macOS. On Windows, run `npm run ci` first, then run `npm run ci:full` from WSL inside the checkout.
109
111
 
110
112
  ## License
111
113
 
package/README.md CHANGED
@@ -19,13 +19,13 @@
19
19
  npm 安装钉死版本:
20
20
 
21
21
  ```bash
22
- dsh plugin --profile web add npm:@chengzhao-dev/dsh-cpp-workflow@0.5.4
22
+ dsh plugin --profile web add npm:@chengzhao-dev/dsh-cpp-workflow@0.5.5
23
23
  ```
24
24
 
25
25
  同版本的 Git 标签:
26
26
 
27
27
  ```bash
28
- dsh plugin --profile web add github:chengzhao-dev/dsh-cpp-workflow#v0.5.4
28
+ dsh plugin --profile web add github:chengzhao-dev/dsh-cpp-workflow#v0.5.5
29
29
  ```
30
30
 
31
31
  桌面应用独占 `desktop` profile。命令行 `add` 被拒绝时,在应用设置里添加插件,地址填同一个 npm 版本。Git 标签安装使用仓库内已构建的 `lib/`,不在目标机器上重新构建;包内也不含需要授权执行的安装脚本。
@@ -103,9 +103,11 @@ npm ci
103
103
  npm test # 构建、三技能结构检查、门禁/环境/schema/注释测试
104
104
  npm run test:matrix # 四套模板 × 五档标准的真实构建矩阵
105
105
  npm run pack:check # 核对 npm 包内容
106
+ npm run ci # 复现 Ubuntu CI:安装、测试、打包检查
107
+ npm run ci:full # 在 ci 之上再跑标准矩阵,需 POSIX 宿主
106
108
  ```
107
109
 
108
- 源码在 `src/`,`npm run build` 生成 `lib/`。结构检查在 `scripts/checking-skill-links.mjs`;测试不调用本机编译器,环境检查用固定版本字符串。标准矩阵需要 POSIX 宿主上的 `cmake`、`ninja`、`perl` 和 `clang++`(或 `CXX` 指定的编译器),在 WSL/Linux/macOS 上运行。
110
+ 源码在 `src/`,`npm run build` 生成 `lib/`。结构检查在 `scripts/checking-skill-links.mjs`;测试不调用本机编译器,环境检查用固定版本字符串。标准矩阵需要 POSIX 宿主上的 `cmake`、`ninja`、`perl` 和 `clang++`(或 `CXX` 指定的编译器),在 WSL/Linux/macOS 上运行。Windows 上先跑 `npm run ci`,再从 WSL 进入仓库跑 `npm run ci:full`。
109
111
 
110
112
  ## 许可
111
113
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chengzhao-dev/dsh-cpp-workflow",
3
- "version": "0.5.4",
3
+ "version": "0.5.5",
4
4
  "description": "C++11–C++23 工程流程技能包:源码四步、CMake 三步与构建三步,按项目声明的环境组合校验 · C++11–C++23 workflow skills for DeepSeek Harness",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -43,6 +43,8 @@
43
43
  "scripts": {
44
44
  "build": "tsc -p tsconfig.json",
45
45
  "test": "npm run build && node scripts/checking-skill-links.mjs && node skills/building-cpp/scripts/checking-encoding.mjs --quiet && node scripts/checking-github-workflows.mjs && node --test tests/*.test.mjs",
46
+ "ci": "npm ci && npm test && npm run pack:check",
47
+ "ci:full": "npm run ci && npm run test:matrix",
46
48
  "check:encoding": "node skills/building-cpp/scripts/checking-encoding.mjs",
47
49
  "check:workflows": "node scripts/checking-github-workflows.mjs",
48
50
  "test:matrix": "bash scripts/checking-standard-matrix.sh",
@@ -14,11 +14,10 @@
14
14
  **好**:
15
15
 
16
16
  ```cpp
17
- //===----------------------------------------------------------------------===//
18
- // board.h - 定义井字棋棋盘、格子坐标与格子类型。
19
- //
20
- // 棋盘保存九个棋盘格子的状态,提供只读查询、落子与整盘清空。
21
- //===----------------------------------------------------------------------===//
17
+ /// @file board.h
18
+ /// @brief 定义井字棋棋盘、格子坐标与格子类型。
19
+ ///
20
+ /// 棋盘保存九个棋盘格子的状态,提供只读查询、落子与清空棋盘。
22
21
  ```
23
22
 
24
23
  文件头只介绍这个文件里有什么,不写作者和变更史,也不把读者指去另一个文件。
@@ -310,11 +309,10 @@ Standard: Latest
310
309
  **好**:
311
310
 
312
311
  ```cpp
313
- //===----------------------------------------------------------------------===//
314
- // board.h - 棋盘状态与规则判断
315
- //
316
- // 定义 Board 的查询、落子与整盘清空。
317
- //===----------------------------------------------------------------------===//
312
+ /// @file board.h
313
+ /// @brief 棋盘状态与规则判断。
314
+ ///
315
+ /// 定义 Board 的查询、落子与清空棋盘。
318
316
  ```
319
317
 
320
318
  变更史进提交说明,外部资料进文档,团队流程进规范文件;注释只写这个文件或这个类型自己的内容,不把读者指去别的文件。
@@ -100,7 +100,7 @@ GameResult result_ = GameResult::kInProgress;
100
100
 
101
101
  | 层 | 行数 | 第一句 | 写什么 |
102
102
  | --- | --- | --- | --- |
103
- | 文件头 | 3–6 | 文件名 - 这个文件定义或实现什么 | 本文件由哪几部分组成 |
103
+ | 文件头 | 3–7 | `@file` 文件名 + `@brief` 一句话 | 本文件由哪几部分组成 |
104
104
  | 类型注释 | 2–14 | 名词短语:这个类型或取值是什么 | 只有类的不变量 |
105
105
  | 特殊成员 | 1–3 | 标签(默认/拷贝/移动构造函数、析构函数) | 后置条件或职责 |
106
106
  | 函数注释 | 3–10 | 正常返回什么、失败时返回或抛什么、检查什么 | 参数与失败方式 |
@@ -109,29 +109,28 @@ GameResult result_ = GameResult::kInProgress;
109
109
 
110
110
  ## 布局:放在哪一层、哪一行
111
111
 
112
- - `///` 只给声明:头文件里的类型、函数、枚举值、公开与私有数据成员。数据成员的主注释写在上一行;行尾 `///<` 只用于枚举项和 `struct` 的短字段。调用方和 Doxygen 从这里取契约。
113
- - `//` 给实现:函数体内的阶段与边界说明、局部变量、文件头横幅、配置文件的说明。
114
- - 不给局部变量和函数体里的语句写 `///`;文件头也不写成 `///`,否则编辑器会把它挂到紧随其后的第一个声明上。
115
- - 判断看一句话写的是「调用方要遵守的承诺」还是「维护者要知道的写法」:前者用 `///`,后者用 `//`。
112
+ - `///` 给声明与文件头:头文件里的类型、函数、枚举值、公开与私有数据成员,以及文件头的 `@file`/`@brief` 块。数据成员的主注释写在上一行;行尾 `///<` 只用于枚举项和 `struct` 的短字段。调用方和 Doxygen 从这里取契约。
113
+ - `///` 也给实现:函数体内的阶段与边界说明、局部变量,一律用 `///`,不混 `//`。
114
+ - `//` 只留给工具指令与别种文件:`// NOLINTNEXTLINE(...)`、`#endif // MACRO` 这类要被工具读取的标记,以及 CMake、YAML 等非 C++ 文件。
115
+ - 判断看位置:声明写契约,实现写理由,两者都用 `///`;`//` 不再表示「实现注释」。
116
116
  - 位置固定:文件头在最上方;类型注释紧贴类型声明;特殊成员(构造、析构、拷贝与移动)排在其他函数之前;函数注释紧贴函数声明;数据成员的主注释紧贴声明上一行,不写行尾补充。
117
117
  - 类数据成员(含私有)的主注释用 `///`;行尾 `///<` 只给枚举项和 `struct` 的短字段。
118
118
  - 注释和它说明的声明之间不留空行;同一个声明块内,空行只用来分组。
119
119
 
120
120
  ### 文件头
121
121
 
122
- 每个手写源文件开头写一段文件头:第一行是「文件名 - 本文件定义或实现什么」,后面只写本文件自己的组成部分或边界。删掉把读者指去另一个头文件、另一个目录或另一层的句子,也删掉「这一层不负责什么」。不写作者、版权年份、变更日志和外部资料路径。
122
+ 每个手写源文件开头写一段 doxygen 文件头:第一行 `/// @file <文件名>`,第二行 `/// @brief` 一句话说明这个文件定义或实现什么,用一行 `///` 隔开后,只写本文件自己的组成部分或边界。删掉把读者指去另一个头文件、另一个目录或另一层的句子,也删掉「这一层不负责什么」。不写作者、版权年份、变更日志和外部资料路径。
123
123
 
124
124
  ```cpp
125
- //===----------------------------------------------------------------------===//
126
- // board.h - 定义井字棋棋盘、格子坐标与格子类型。
127
- //
128
- // 棋盘保存九个棋盘格子的状态,提供只读查询、落子与整盘清空。
129
- //===----------------------------------------------------------------------===//
125
+ /// @file board.h
126
+ /// @brief 定义井字棋棋盘、格子坐标与格子类型。
127
+ ///
128
+ /// 棋盘保存九个棋盘格子的状态,提供只读查询、落子与清空棋盘。
130
129
  ```
131
130
 
132
- - C++ 用 `//`,CMake 用 `#`,YAML/JSON 用 `#` 或 `//`。
131
+ - C++ 源文件用以上格式;CMake 用 `#`,YAML/JSON 用 `#`。
133
132
  - 超过约 200 行的文件用章节横幅分组,组名是读者要跳转的锚点;一个文件不超过 8 个横幅。
134
- - 文件头 3–6 行;第一行点文件名,后面只写本文件的范围与组成,不写实现步骤、作者和变更史。
133
+ - 文件头 3–7 行;`@file` 与 `@brief` 各占一行,后面只写本文件的范围与组成,不写实现步骤、作者和变更史。
135
134
 
136
135
  ## 排版:符号、宽度、横幅与标签
137
136
 
@@ -46,11 +46,10 @@
46
46
  5. `#endif // MACRO`
47
47
 
48
48
  ```cpp
49
- //===----------------------------------------------------------------------===//
50
- // thing_table.h - 定义 ThingTable 与它用到的状态。
51
- //
52
- // 头文件放常量、枚举、结构体与类声明。
53
- //===----------------------------------------------------------------------===//
49
+ /// @file thing_table.h
50
+ /// @brief 定义 ThingTable 与它用到的状态。
51
+ ///
52
+ /// 头文件放常量、枚举、结构体与类声明。
54
53
 
55
54
  #ifndef PROJECT_PATH_FILE_H_
56
55
  #define PROJECT_PATH_FILE_H_
@@ -99,9 +98,8 @@ class ThingTable {
99
98
  4. 命名空间与定义
100
99
 
101
100
  ```cpp
102
- //===----------------------------------------------------------------------===//
103
- // thing_table.cpp - 实现 ThingTable 的添加与查询。
104
- //===----------------------------------------------------------------------===//
101
+ /// @file thing_table.cpp
102
+ /// @brief 实现 ThingTable 的添加与查询。
105
103
 
106
104
  #include "project/path/thing.h"
107
105
 
@@ -25,8 +25,10 @@ const SKIP_DIR = new Set([
25
25
  '.tmp', 'temp', 'third_party', 'third-party', 'external', 'vendor', 'deps', 'cmake-build-debug',
26
26
  'cmake-build-release', '.vs', '.idea',
27
27
  ])
28
- const FORBIDDEN_TAGS = ['@returns', '@brief', '@details', '@author', '@since', '@todo', '@date', '@file', '@copyright', '@version']
28
+ const FORBIDDEN_TAGS = ['@returns', '@details', '@author', '@since', '@todo', '@date', '@copyright', '@version']
29
29
  const BANNER = /^\/\/=+-+=+\/\/$/
30
+ const DOXY_FILE = /^\/\/\/\s*@file\s+\S/
31
+ const DOXY_BRIEF = /^\/\/\/\s*@brief\s+\S/
30
32
  const MEMBER_LIST = /提供[^。]{0,40}、[^。]{0,40}(的功能|的能力|的接口)|功能[::]/
31
33
  const RATIONALE = /而不是|而非|代价.{0,4}忽略|只留一份|与其|比.{0,12}更|更(?:安全|快|清楚|直接|好写|难写)/
32
34
  const FORWARD_REF = /(?:留在|归|交给|由)\s*`?[A-Z][A-Za-z0-9_]*`?/
@@ -205,16 +207,29 @@ export function checkText(text, options = {}) {
205
207
  const inClass = classMemberLines(lines)
206
208
 
207
209
  const firstContent = lines.findIndex((line) => line.trim() !== '')
208
- if (requireHeader && firstContent !== -1 && !BANNER.test(lines[firstContent].trim())) {
209
- add(firstContent + 1, 'header-missing', '文件开头缺少文件头横幅(//===...===//)')
210
+ const firstTrimmed = firstContent === -1 ? '' : lines[firstContent].trim()
211
+ const hasDoxyHeader = DOXY_FILE.test(firstTrimmed)
212
+ let doxyHeaderEnd = -1
213
+ if (hasDoxyHeader) {
214
+ doxyHeaderEnd = firstContent
215
+ while (doxyHeaderEnd + 1 < lines.length && lines[doxyHeaderEnd + 1].trim().startsWith('///')) doxyHeaderEnd += 1
210
216
  }
211
- if (firstContent !== -1 && BANNER.test(lines[firstContent].trim())) {
217
+ if (requireHeader && firstContent !== -1 && !BANNER.test(firstTrimmed) && !hasDoxyHeader) {
218
+ add(firstContent + 1, 'header-missing', '文件开头缺少文件头:写 /// @file <文件名>,下一行写 /// @brief')
219
+ }
220
+ if (firstContent !== -1 && (BANNER.test(firstTrimmed) || hasDoxyHeader)) {
212
221
  const own = options.fileName ? options.fileName.split(/[\\/]/).pop() : ''
222
+ const bannerHeader = BANNER.test(firstTrimmed)
223
+ if (hasDoxyHeader && !DOXY_BRIEF.test((lines[firstContent + 1] ?? '').trim())) {
224
+ add(firstContent + 2, 'header-brief', '文件头第二行缺少 /// @brief 一句话说明')
225
+ }
213
226
  for (let index = firstContent + 1; index < lines.length; index += 1) {
214
227
  const trimmed = lines[index].trim()
215
- if (BANNER.test(trimmed) || !trimmed.startsWith('//')) break
228
+ if (bannerHeader ? BANNER.test(trimmed) || !trimmed.startsWith('//') : !trimmed.startsWith('///')) break
216
229
  const names = trimmed.match(/\b[A-Za-z0-9_-]+\.(?:h|hpp|cpp|cc)\b/g) ?? []
217
- for (const name of names) {
230
+ const fileMatches = trimmed.match(/@file\s+(\S+)/)
231
+ const namesToCheck = fileMatches ? names.filter((name) => name !== fileMatches[1]) : names
232
+ for (const name of namesToCheck) {
218
233
  if (name !== own) add(index + 1, 'header-cross-file', `文件头提到了别的源文件 ${name}:只介绍本文件,删掉跨文件句子`)
219
234
  }
220
235
  if (/\bapps\//.test(trimmed)) add(index + 1, 'header-cross-file', '文件头指向了另一个目录:只介绍本文件')
@@ -238,6 +253,10 @@ export function checkText(text, options = {}) {
238
253
  }
239
254
 
240
255
  for (let index = 0; index < lines.length; index += 1) {
256
+ if (doxyHeaderEnd !== -1 && index <= doxyHeaderEnd) {
257
+ index = doxyHeaderEnd
258
+ continue
259
+ }
241
260
  if (!lines[index].trim().startsWith('///')) continue
242
261
  if (lines[index].trim().startsWith('///<')) continue
243
262
  const start = index