create-dwy 0.25.2 → 0.26.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 (34) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/package.json +1 -1
  3. package/src/sync-plan.js +1 -1
  4. package/src/sync.js +1 -0
  5. package/src/tab-multiselect.js +2 -2
  6. package/templates/ai-tools/CHANGELOG.md +10 -0
  7. package/templates/ai-tools/VERSION +1 -1
  8. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-commercial-sdk/SKILL.md +20 -0
  9. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-github-action-publish/SKILL.md +1 -1
  10. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/SKILL.md +6 -2
  11. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/android.md +2 -2
  12. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/backend-deploy.md +1 -1
  13. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/frontend-deploy.md +1 -1
  14. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/generic-release.md +2 -2
  15. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/harmony.md +2 -2
  16. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/ios.md +2 -2
  17. package/templates/ai-tools/skills//351/200/232/347/224/250/dwy-dependency-choice/SKILL.md +2 -2
  18. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/SKILL.md +0 -244
  19. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/assets/files-whitelist-snippet.json +0 -22
  20. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/assets/js-commercial.npmignore +0 -76
  21. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/assets/python-commercial-MANIFEST.in +0 -37
  22. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/assets/python-commercial.gitignore +0 -62
  23. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/references/js-commercial-guide.md +0 -133
  24. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/references/publish-blacklist.md +0 -106
  25. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/references/python-commercial-guide.md +0 -147
  26. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/references/sensitive-patterns.md +0 -112
  27. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/scripts/check_comments.py +0 -260
  28. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/scripts/check_js_publish.sh +0 -110
  29. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/scripts/check_python_publish.sh +0 -171
  30. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/scripts/detect_sdk.sh +0 -71
  31. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/scripts/init_config.sh +0 -91
  32. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-semver/SKILL.md +0 -136
  33. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-semver/references/semver-spec.md +0 -70
  34. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-semver/scripts/bump.py +0 -137
@@ -1,91 +0,0 @@
1
- #!/usr/bin/env bash
2
- # dwy-sdk-spec 配置缓存读写
3
- # 缓存写项目内 <project_root>/.dwy/sdk-spec/config.json,扁平结构、不存绝对路径
4
- # 用法:
5
- # init_config.sh get <project_root>
6
- # init_config.sh set <project_root> <edition> <languages_json> <sdk_paths_json>
7
- # init_config.sh reset <project_root>
8
-
9
- set -euo pipefail
10
-
11
- config_file() {
12
- echo "${1%/}/.dwy/sdk-spec/config.json"
13
- }
14
-
15
- ensure_config() {
16
- local root="$1"
17
- local dir="${root%/}/.dwy/sdk-spec"
18
- local file
19
- file="$(config_file "$root")"
20
- mkdir -p "$dir"
21
- if [[ ! -f "$file" ]]; then
22
- echo '{"version":"1"}' > "$file"
23
- fi
24
- # 检测损坏
25
- if ! jq empty "$file" 2>/dev/null; then
26
- echo "ERROR: $file is corrupted. Back it up and delete to reset." >&2
27
- exit 2
28
- fi
29
- }
30
-
31
- cmd_get() {
32
- local root="$1"
33
- ensure_config "$root"
34
- # 无 edition 视为未配置,返回 null
35
- jq 'if has("edition") then . else null end' "$(config_file "$root")"
36
- }
37
-
38
- cmd_set() {
39
- local root="$1" edition="$2" languages_json="$3" sdk_paths_json="$4"
40
-
41
- if [[ "$edition" != "commercial" && "$edition" != "opensource" ]]; then
42
- echo "ERROR: edition must be 'commercial' or 'opensource', got: $edition" >&2
43
- exit 2
44
- fi
45
-
46
- ensure_config "$root"
47
- local now file tmp
48
- now=$(date -u +%Y-%m-%dT%H:%M:%SZ)
49
- file="$(config_file "$root")"
50
- tmp=$(mktemp)
51
- jq -n \
52
- --arg e "$edition" \
53
- --argjson l "$languages_json" \
54
- --argjson s "$sdk_paths_json" \
55
- --arg t "$now" \
56
- '{version:"1", edition:$e, languages:$l, sdk_paths:$s, configured_at:$t}' \
57
- > "$tmp"
58
- mv "$tmp" "$file"
59
-
60
- echo "OK: cached $root -> $edition"
61
- echo "Remember to add .dwy/ to .gitignore (本机判断缓存,不入版本库)"
62
- }
63
-
64
- cmd_reset() {
65
- local root="$1"
66
- local file
67
- file="$(config_file "$root")"
68
- rm -f "$file"
69
- echo "OK: removed $file"
70
- }
71
-
72
- main() {
73
- if [[ $# -lt 1 ]]; then
74
- echo "Usage: $0 {get|set|reset} <project_root> [args...]" >&2
75
- exit 2
76
- fi
77
- local action="$1"
78
- shift
79
-
80
- case "$action" in
81
- get) cmd_get "$@" ;;
82
- set) cmd_set "$@" ;;
83
- reset) cmd_reset "$@" ;;
84
- *)
85
- echo "ERROR: unknown action: $action" >&2
86
- exit 2
87
- ;;
88
- esac
89
- }
90
-
91
- main "$@"
@@ -1,136 +0,0 @@
1
- ---
2
- name: dwy-semver
3
- description: "语义化版本号(SemVer 2.0.0)决策助手。涉及以下任何场景必须使用此 skill(即使用户没说 'semver'):用户问'这个改动该升大版本还是小版本'、'该 bump major/minor/patch 吗'、'这个改动算不算 breaking change'、'版本号怎么定'、'1.2.3 下一个版本是多少'、'加了个新功能版本怎么走'、'0.x 阶段版本怎么算'、'预发布/alpha/beta/rc 版本号怎么写';用户描述了一段代码变更想知道对应什么版本级别;用户在发版前纠结版本号。本 skill 只负责'版本号该升哪一位 + 算出具体号',不负责执行发版流程(那是 dwy-publish)。"
4
- ---
5
-
6
- # 语义化版本决策(SemVer 2.0.0)
7
-
8
- 判断一次变更该升 **MAJOR / MINOR / PATCH** 的哪一位,并算出具体版本号。规则源自 [semver.org](https://semver.org/) 2.0.0 官方规范。
9
-
10
- 本 skill 解决的核心难点不是"加 1",而是**判断一个变更到底算 breaking、feature 还是 fix**——这一步判错,版本号语义就废了。
11
-
12
- ## 版本号格式
13
-
14
- `MAJOR.MINOR.PATCH`,如 `2.1.3`。三位都是非负整数。可选附加 `-预发布` 和 `+构建元数据`,如 `1.0.0-rc.1+20260521`。
15
-
16
- ## 核心决策表
17
-
18
- 前提:SemVer 围绕**公开 API**(对外导出的函数/组件/类型/CLI 参数/HTTP 接口/配置项)定义。判断"兼容性"永远站在**依赖方**视角——升级后调用方代码不改还能跑 = 兼容。
19
-
20
- | 级别 | 触发条件 | 含义 |
21
- |------|---------|------|
22
- | **MAJOR** | 向后**不兼容**的公开 API 变更 | 调用方升级后**可能要改代码** |
23
- | **MINOR** | 向后兼容地**新增**功能,或标记 deprecated | 调用方不改代码,多了能力 |
24
- | **PATCH** | 向后兼容的 **bug 修复** | 调用方无感,行为回到本应正确的样子 |
25
-
26
- **归零规则**(升高位时低位清零,最易错):
27
- - 升 MAJOR:minor、patch 都归零 —— `1.4.2 → 2.0.0`
28
- - 升 MINOR:patch 归零 —— `1.4.2 → 1.5.0`
29
- - 升 PATCH:直接 +1 —— `1.4.2 → 1.4.3`
30
-
31
- 不要手算,用 `scripts/bump.py`(见下方「算出具体版本号」)。
32
-
33
- ## breaking change 判断清单(最关键)
34
-
35
- ### 一定是 MAJOR(破坏兼容)
36
- - 删除 / 重命名 公开的函数、组件、类型、字段、CLI 参数、配置项
37
- - 改函数签名:删参数、调整参数顺序、**新增必填参数**
38
- - 改返回值结构 / 类型(删字段、改字段类型、改字段含义)
39
- - 改默认行为(同样的输入产出不同结果,且旧行为是被依赖的)
40
- - **加严校验**:原来能通过的输入现在报错(如新增必填字段、收紧取值范围)
41
- - 删除 / 修改 已有枚举值
42
- - 移除对某运行环境 / 依赖版本的支持(如最低 Node 版本抬高)
43
-
44
- ### 一定是 MINOR(兼容新增)
45
- - 新增函数 / 组件 / 类型 / 可选参数 / 可选返回字段
46
- - 新增**带默认值**的配置项
47
- - 标记某 API 为 deprecated(仅警告,未删除)
48
- - 放宽校验:原来报错的输入现在也能通过
49
-
50
- ### 一定是 PATCH(兼容修复)
51
- - 修内部实现 bug,公开 API 签名与契约不变
52
- - 不改 API 的性能优化、内部重构
53
- - 文档 / 注释 / 类型声明修正(不改运行时行为)
54
- - 依赖的 patch 级升级(不波及自身 API)
55
-
56
- ### 边界 case(想清楚再定)
57
- - **bug 修复改变了已有行为**:若有调用方依赖那个"错误"行为,修复对它们就是 breaking → 倾向 **MAJOR**(或至少在 CHANGELOG 显著标注)。问自己:"有人靠这个 bug 干活吗?"
58
- - **新增枚举值**:若调用方代码对枚举做了穷举(switch 全分支 / 模式匹配),新值可能让其逻辑漏处理 → 视契约严格度,保守按 MINOR 但 CHANGELOG 提示
59
- - **类型收窄**(TS 把 `string` 改成 `'a' | 'b'`):原来传任意 string 的调用方会编译失败 → MAJOR
60
- - **修改错误信息文案 / 日志**:通常 PATCH;除非有人解析它(不该依赖,但现实存在)
61
-
62
- 判不准时,问:**"一个只读了 CHANGELOG 标题的用户,升级后会不会被意外搞坏?"** 会 → MAJOR。
63
-
64
- ## 0.y.z 初始开发阶段(特殊规则)
65
-
66
- 主版本号为 0(如 `0.9.1`)表示**公开 API 尚不稳定**,规范允许此阶段任何变更随时发生。`1.0.0` 才正式定义稳定 API。
67
-
68
- 社区惯例(也是 dwy-shared 内 ekit/eapi/cli 当前所处阶段):
69
- - **breaking 变更** → 升 minor:`0.9.1 → 0.10.0`
70
- - **feature / fix** → 升 patch:`0.9.1 → 0.9.2`
71
- - 准备好对外稳定承诺时,发 `1.0.0`
72
-
73
- 即 0.x 阶段把 minor 当"大版本"、patch 当"小版本"用。
74
-
75
- ## 决策流程
76
-
77
- 1. **划清公开 API 边界**:这次改的东西对外可见吗?纯内部改动通常只到 PATCH
78
- 2. **判兼容性**:对照上方清单,是 breaking / 新增 / 修复 哪一类
79
- 3. **看是否 0.x**:当前主版本是 0 → 套用 0.y.z 惯例
80
- 4. **是否预发布**:要先发 alpha/beta/rc 试水 → 见下
81
- 5. **算具体号**:用 `scripts/bump.py`,别手算归零
82
-
83
- ## 预发布与构建元数据(简述)
84
-
85
- - **预发布**:正式版前的试验版,如 `1.0.0-alpha`、`1.0.0-beta.2`、`1.0.0-rc.1`。优先级**低于**同号正式版:`1.0.0-rc.1 < 1.0.0`
86
- - **构建元数据**:`+` 后接构建信息,如 `1.0.0+20260521`,**不参与**版本比较,仅作标记
87
-
88
- 预发布优先级排序、版本比较 11 条规则等细节见 [references/semver-spec.md](references/semver-spec.md)。
89
-
90
- ## 正式版与预发布版
91
-
92
- 按用户意图和目标仓库的版本及发布约定判断正式版或预发布版,不根据分支判断。测试版本按项目惯例使用 `beta`、`rc` 等标识,正式版本不带预发布标识。判断不明确时询问用户。
93
-
94
- ## 算出具体版本号
95
-
96
- `scripts/bump.py` 按 SemVer 归零规则与 node-semver 预发布语义计算,避免手算出错:
97
-
98
- ```bash
99
- python3 scripts/bump.py <当前版本> <级别> [--preid alpha|beta|rc]
100
- ```
101
-
102
- 级别:`major | minor | patch | premajor | preminor | prepatch | prerelease`
103
-
104
- ```
105
- python3 scripts/bump.py 1.2.3 minor # -> 1.3.0
106
- python3 scripts/bump.py 0.9.1 minor # -> 0.10.0 (0.x breaking)
107
- python3 scripts/bump.py 1.0.0 preminor --preid rc # -> 1.1.0-rc.0
108
- python3 scripts/bump.py 1.1.0-rc.0 prerelease # -> 1.1.0-rc.1
109
- python3 scripts/bump.py 2.0.0-rc.1 major # -> 2.0.0 (预发布转正式)
110
- ```
111
-
112
- ## 输出格式
113
-
114
- 给用户决策时,固定回三件事:
115
-
116
- 1. **级别**:MAJOR / MINOR / PATCH(+ 是否预发布)
117
- 2. **新版本号**:具体数字(用 bump.py 算)
118
- 3. **理由**:依据清单里的哪一条,一句话说清为什么这次变更属于该级别
119
-
120
- 例:
121
- > 变更:给 `createRequest()` 新增了一个带默认值的可选参数 `timeout`。
122
- > → **MINOR**,`2.2.1 → 2.3.0`。理由:新增可选参数,旧调用方不传也照常工作,向后兼容的功能新增。
123
-
124
- ## 与 dwy-publish 的边界
125
-
126
- - **dwy-semver**(本 skill):只回答"该升哪一位、新号是多少"
127
- - **dwy-publish**:管理工程版本、changelog 和 tag;是否构建和发布按项目上下文及用户授权判断。
128
-
129
- 发版时若不确定版本号,先用本 skill 决策,再交给 dwy-publish 落地。
130
-
131
- ## 风格
132
-
133
- - 中文回答
134
- - 判级别必给**理由**,指向清单具体条目
135
- - 版本号一律用 `bump.py` 算,不手算归零
136
- - 引用规范细节附来源:`(来源:https://semver.org/)`
@@ -1,70 +0,0 @@
1
- # SemVer 2.0.0 规范细节
2
-
3
- > 来源:https://semver.org/ ,缓存于 2026-05-21。本文件是 SKILL.md 的补充,覆盖预发布优先级、版本比较、构建元数据、0.y.z 与常见误区。决策主流程见 [../SKILL.md](../SKILL.md)。
4
-
5
- ## 1. 版本号构成(规范 §2、§9、§10)
6
-
7
- 完整形态:`MAJOR.MINOR.PATCH[-预发布][+构建元数据]`
8
-
9
- - `MAJOR` / `MINOR` / `PATCH`:非负整数,**禁止前导零**(`01` 非法)
10
- - 预发布(pre-release):紧跟 `-`,由点分标识符组成,如 `-alpha`、`-alpha.1`、`-0.3.7`、`-x.7.z.92`
11
- - 每个标识符仅含 `[0-9A-Za-z-]`,不可为空
12
- - 纯数字标识符**禁止前导零**
13
- - 构建元数据(build metadata):紧跟 `+`,同样点分标识符,如 `+001`、`+20130313144700`、`+exp.sha.5114f85`
14
-
15
- ## 2. 递增规则(规范 §6、§7、§8)
16
-
17
- - §6 **PATCH**(`z`,x.y.Z):仅当引入**向后兼容的 bug 修复**时递增
18
- - §7 **MINOR**(`y`,x.Y.z):新增**向后兼容**的功能时**必须**递增;公开 API 中任何功能被标记为 deprecated **必须**递增;也**可**在新增大量内部新功能时递增。递增时 patch **必须**归零
19
- - §8 **MAJOR**(`x`,X.y.z):引入**向后不兼容**的 API 变更时**必须**递增。递增时 minor、patch **必须**归零。可包含 minor、patch 级别的变更
20
-
21
- ## 3. 主版本 0(规范 §4、§5)
22
-
23
- - §4:`0.y.z` 用于初始开发,**任何东西都可能随时变化**,公开 API **不应**视为稳定
24
- - §5:`1.0.0` 定义公开 API。此后版本号如何递增由这套 API 的变化决定
25
-
26
- ## 4. 发布不可变(规范 §3)
27
-
28
- 一旦某个版本的包发布,其内容**禁止修改**。任何修改都**必须**作为新版本发布。这是版本号可被依赖的前提。
29
-
30
- ## 5. 预发布优先级(规范 §9、§11)
31
-
32
- 预发布版本表示**可能不稳定、未必满足正式版兼容要求**。其优先级**低于**关联的正式版:
33
-
34
- ```
35
- 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta
36
- < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0
37
- ```
38
-
39
- ## 6. 版本优先级比较规则(规范 §11)
40
-
41
- 判断两个版本谁"新":
42
-
43
- 1. **§11.2** 先比 MAJOR、MINOR、PATCH,按数值依次比较:`1.0.0 < 2.0.0 < 2.1.0 < 2.1.1`
44
- 2. **§11.3** major/minor/patch 相同时,**有预发布的一方优先级更低**:`1.0.0-alpha < 1.0.0`
45
- 3. **§11.4** 两者都有预发布时,从左到右逐个标识符比较:
46
- - 纯数字标识符按**数值**比较
47
- - 含字母/连字符的标识符按 **ASCII 字典序**比较
48
- - **数字标识符** 优先级**低于** 非数字标识符(`alpha.1` 中 `1` < `alpha`)
49
- - 前面都相等时,**字段更多**的一方优先级更高(`alpha < alpha.1`)
50
-
51
- **构建元数据不参与比较**(§10):`1.0.0+build.1`、`1.0.0+build.2`、`1.0.0` 优先级相同。
52
-
53
- ## 7. 常见误区
54
-
55
- | 误区 | 纠正 |
56
- |------|------|
57
- | "改了代码就得升 patch" | 纯内部、无 API 影响的改动也可不发版;发版才需定级别 |
58
- | "加功能一定升 minor" | 若新功能破坏了旧用法(如改了默认行为),是 MAJOR |
59
- | "bug 修复一定是 patch" | 若有人依赖旧(错误)行为,修复对其是 breaking,倾向 MAJOR |
60
- | "0.x 可以随便发" | 规范允许,但社区惯例仍区分 minor=breaking、patch=feature/fix |
61
- | "1.0.0-rc.1 比 1.0.0 新" | 反了,预发布优先级低于正式版 |
62
- | "构建元数据能区分版本新旧" | 不能,比较时被忽略 |
63
- | "版本号能跳"(1.0.0 → 1.0.5) | 不能,必须逐级递增 |
64
-
65
- ## 8. 与 npm/node-semver 的关系
66
-
67
- `scripts/bump.py` 的预发布递增语义对齐 [node-semver](https://github.com/npm/node-semver) 的 `semver.inc()`:
68
- - `premajor/preminor/prepatch`:先递增对应位再加 `-preid.0`
69
- - `prerelease`:无预发布时等价 `prepatch` 加预发布;已有预发布时递增末位数字,或换 preid 重置为 0
70
- - 升 `major/minor/patch` 时若当前正好是对应位的预发布版(如 `2.0.0-rc.1` 升 major),仅转为正式版而不再 +1
@@ -1,137 +0,0 @@
1
- #!/usr/bin/env python3
2
- """SemVer 2.0.0 版本号递增计算器。
3
-
4
- 给定当前版本号 + 递增级别,输出下一个版本号,严格遵循 SemVer 归零规则
5
- 与 node-semver 的预发布递增语义。纯标准库,无外部依赖。
6
-
7
- 为什么需要这个脚本:归零(minor+1 → patch 归零)和预发布递增(rc.0 → rc.1)
8
- 是 LLM 手算高频出错点,交给确定性代码避免算错。
9
-
10
- 用法:
11
- bump.py 1.2.3 minor -> 1.3.0
12
- bump.py 1.2.3 major -> 2.0.0
13
- bump.py 0.9.1 minor -> 0.10.0
14
- bump.py 1.0.0 prerelease --preid rc -> 1.0.1-rc.0
15
- bump.py 1.0.0 preminor --preid rc -> 1.1.0-rc.0
16
- bump.py 1.1.0-rc.0 prerelease -> 1.1.0-rc.1
17
-
18
- 级别: major | minor | patch | premajor | preminor | prepatch | prerelease
19
- """
20
-
21
- import argparse
22
- import re
23
- import sys
24
-
25
- # 不含 build metadata 的解析;bump 时 build metadata(+xxx)按 SemVer 一律丢弃
26
- _VERSION_RE = re.compile(
27
- r"^(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)"
28
- r"(?:-(?P<prerelease>[0-9A-Za-z.-]+))?"
29
- r"(?:\+(?P<build>[0-9A-Za-z.-]+))?$"
30
- )
31
-
32
-
33
- def parse(version: str) -> tuple[int, int, int, list]:
34
- """解析版本号为 (major, minor, patch, prerelease 标识符列表)。
35
-
36
- prerelease 中纯数字段转 int(便于递增与比较),其余保留字符串。
37
- 无效格式直接抛 ValueError,让调用方感知,不静默兜底。
38
- """
39
- m = _VERSION_RE.match(version.strip())
40
- if not m:
41
- raise ValueError(f"非法 SemVer 版本号: {version!r}")
42
- pre_raw = m.group("prerelease")
43
- prerelease = []
44
- if pre_raw:
45
- for part in pre_raw.split("."):
46
- prerelease.append(int(part) if part.isdigit() else part)
47
- return int(m.group("major")), int(m.group("minor")), int(m.group("patch")), prerelease
48
-
49
-
50
- def _format(major: int, minor: int, patch: int, prerelease: list) -> str:
51
- base = f"{major}.{minor}.{patch}"
52
- if prerelease:
53
- base += "-" + ".".join(str(p) for p in prerelease)
54
- return base
55
-
56
-
57
- def _bump_prerelease(prerelease: list, preid: str | None) -> list:
58
- """递增已有的预发布标识符列表(node-semver 语义)。
59
-
60
- - 指定了 preid 且与当前不同 -> 切到新 preid,从 0 开始
61
- - 否则递增最后一个数字段;末位非数字则追加 0
62
- """
63
- if preid and (not prerelease or prerelease[0] != preid):
64
- return [preid, 0]
65
- bumped = list(prerelease)
66
- for i in range(len(bumped) - 1, -1, -1):
67
- if isinstance(bumped[i], int):
68
- bumped[i] += 1
69
- return bumped
70
- bumped.append(0)
71
- return bumped
72
-
73
-
74
- def inc(version: str, release: str, preid: str | None = None) -> str:
75
- """按 release 级别递增版本号,返回新版本字符串。"""
76
- major, minor, patch, prerelease = parse(version)
77
-
78
- if release == "major":
79
- # 已是 x.0.0 的预发布(如 2.0.0-rc.1)升 major 只需转正式版
80
- if prerelease and minor == 0 and patch == 0:
81
- return _format(major, 0, 0, [])
82
- return _format(major + 1, 0, 0, [])
83
-
84
- if release == "minor":
85
- if prerelease and patch == 0:
86
- return _format(major, minor, 0, [])
87
- return _format(major, minor + 1, 0, [])
88
-
89
- if release == "patch":
90
- # 预发布转正式版时 patch 不再 +1(1.2.3-rc.1 -> 1.2.3)
91
- if prerelease:
92
- return _format(major, minor, patch, [])
93
- return _format(major, minor, patch + 1, [])
94
-
95
- pre_start = [preid, 0] if preid else [0]
96
-
97
- if release == "premajor":
98
- return _format(major + 1, 0, 0, pre_start)
99
- if release == "preminor":
100
- return _format(major, minor + 1, 0, pre_start)
101
- if release == "prepatch":
102
- return _format(major, minor, patch + 1, pre_start)
103
-
104
- if release == "prerelease":
105
- # 当前无预发布 -> 等价 prepatch 后加预发布
106
- if not prerelease:
107
- return _format(major, minor, patch + 1, pre_start)
108
- return _format(major, minor, patch, _bump_prerelease(prerelease, preid))
109
-
110
- raise ValueError(f"未知级别: {release!r}")
111
-
112
-
113
- def main() -> int:
114
- parser = argparse.ArgumentParser(
115
- description="SemVer 2.0.0 版本号递增计算器",
116
- formatter_class=argparse.RawDescriptionHelpFormatter,
117
- epilog=__doc__,
118
- )
119
- parser.add_argument("version", help="当前版本号,如 1.2.3 或 1.2.3-rc.0")
120
- parser.add_argument(
121
- "release",
122
- choices=["major", "minor", "patch", "premajor", "preminor", "prepatch", "prerelease"],
123
- help="递增级别",
124
- )
125
- parser.add_argument("--preid", default=None, help="预发布标识符,如 alpha / beta / rc")
126
- args = parser.parse_args()
127
-
128
- try:
129
- print(inc(args.version, args.release, args.preid))
130
- except ValueError as e:
131
- print(f"错误: {e}", file=sys.stderr)
132
- return 1
133
- return 0
134
-
135
-
136
- if __name__ == "__main__":
137
- raise SystemExit(main())