create-dwy 0.21.0 → 0.22.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.
- package/CHANGELOG.md +12 -0
- package/bin/index.js +6 -3
- package/package.json +1 -1
- package/src/cli-help.js +9 -2
- package/src/self-upgrade.js +7 -0
- package/src/skill-store.js +182 -0
- package/src/sync-all.js +134 -32
- package/src/sync-codex.js +15 -5
- package/src/sync-global-skills.js +65 -14
- package/src/sync-opencode.js +9 -5
- package/src/sync.js +13 -4
- package/templates/ai-tools/AGENTS.md +1 -1
- package/templates/ai-tools/CHANGELOG.md +6 -0
- package/templates/ai-tools/VERSION +1 -1
- package/templates/ai-tools/skills/DolphinDB/dwy-dolphindb/references/official/progr/sql/createExternalTable.md +2 -2
- package/templates/ai-tools/skills/DolphinDB/dwy-dolphindb/references/official/tools/superset.md +1 -1
- package/templates/ai-tools/skills/DolphinDB/dwy-dolphindb/references/official/tutorials/Migrate_data_from_InfluxDB_to_DolphinDB.md +1 -1
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/SKILL.md +18 -25
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/android.md +3 -3
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/backend-deploy.md +4 -4
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/changelog.md +6 -70
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/frontend-deploy.md +3 -3
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/generic-release.md +5 -5
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/git-tag.md +4 -79
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/harmony.md +2 -2
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/ios.md +3 -3
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/publish-config.md +4 -4
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/version-sync.md +7 -7
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-semver/SKILL.md +3 -7
- package/templates/ai-tools/skills//351/200/232/347/224/250/dwy-development-review/SKILL.md +58 -0
- package/templates/ai-tools/skills//351/200/232/347/224/250/dwy-development-review/references/delegation-guide.md +46 -0
- package/templates/ai-tools/skills//351/200/232/347/224/250/dwy-development-review/references/review-guide.md +57 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* 只覆盖本次点名的 skill,不删除全局目录里其它个人 skill。
|
|
2
|
+
* 将 Skills 安装为用户共享副本的目录链接,并处理同平台全局优先去重。
|
|
3
|
+
* 只处理模板或同步记录中的条目;个人 Skill 不参与清理,原真实目录先备份。
|
|
5
4
|
*/
|
|
6
5
|
|
|
7
6
|
import fs from 'fs-extra'
|
|
8
7
|
import os from 'node:os'
|
|
9
8
|
import path from 'path'
|
|
10
|
-
import {
|
|
9
|
+
import { logAction, scanExisting } from './sync.js'
|
|
10
|
+
import { linkSkill, prepareSharedSkills, removeSkillEntry } from './skill-store.js'
|
|
11
11
|
|
|
12
12
|
/** 项目目录:随所选平台落到 .claude/skills、.agents/skills 等 */
|
|
13
13
|
export const SKILL_DEST_PROJECT = 'project'
|
|
@@ -115,7 +115,7 @@ export function listSkillDestOptions(homeDir = os.homedir()) {
|
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
/**
|
|
118
|
-
*
|
|
118
|
+
* 将 skill 的共享副本链接到已选全局 dest。destIds 含 project 时跳过。
|
|
119
119
|
* 只写传入的 skills,不清理目标根目录里其它条目。
|
|
120
120
|
*
|
|
121
121
|
* @param {{ skills: Array<{ name: string, sourcePath: string }>, destIds: string[], homeDir?: string }} opts
|
|
@@ -123,15 +123,15 @@ export function listSkillDestOptions(homeDir = os.homedir()) {
|
|
|
123
123
|
*/
|
|
124
124
|
export async function copySkillsToGlobalDirs({ skills, destIds, homeDir = os.homedir() }) {
|
|
125
125
|
let count = 0
|
|
126
|
+
const sharedSkills = await prepareSharedSkills({ skills, homeDir })
|
|
126
127
|
|
|
127
128
|
for (const destId of destIds) {
|
|
128
129
|
const destRoot = resolveGlobalSkillDir(destId, homeDir)
|
|
129
130
|
if (!destRoot) continue
|
|
130
131
|
|
|
131
|
-
for (const skill of
|
|
132
|
+
for (const skill of sharedSkills) {
|
|
132
133
|
const dest = path.join(destRoot, skill.name)
|
|
133
|
-
await
|
|
134
|
-
await fs.copy(skill.sourcePath, dest, { overwrite: true, filter: copyFilter(skill.sourcePath) })
|
|
134
|
+
await linkSkill({ skill, dest, backupRoot: path.join(homeDir, '.dwy', 'skill-backups') })
|
|
135
135
|
logAction(`${DEST_LABELS[destId] || destId}/${skill.name}`)
|
|
136
136
|
count++
|
|
137
137
|
}
|
|
@@ -141,23 +141,23 @@ export async function copySkillsToGlobalDirs({ skills, destIds, homeDir = os.hom
|
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
/**
|
|
144
|
-
*
|
|
144
|
+
* 将 skill 的共享副本链接到项目内已选平台的 skills 目录。
|
|
145
145
|
* 不写 rules / commands / hooks。
|
|
146
146
|
*
|
|
147
|
-
* @param {{ projectDir: string, platforms: string[], skills: Array<{ name: string, sourcePath: string }
|
|
147
|
+
* @param {{ projectDir: string, platforms: string[], skills: Array<{ name: string, sourcePath: string }>, homeDir?: string }} opts
|
|
148
148
|
* @returns {Promise<number>}
|
|
149
149
|
*/
|
|
150
|
-
export async function copySkillsToProjectPlatforms({ projectDir, platforms, skills }) {
|
|
150
|
+
export async function copySkillsToProjectPlatforms({ projectDir, platforms, skills, homeDir }) {
|
|
151
151
|
let count = 0
|
|
152
|
+
const sharedSkills = await prepareSharedSkills({ skills, homeDir })
|
|
152
153
|
|
|
153
154
|
for (const platform of platforms) {
|
|
154
155
|
const destRoot = resolveProjectSkillDir(projectDir, platform)
|
|
155
156
|
if (!destRoot) continue
|
|
156
157
|
|
|
157
|
-
for (const skill of
|
|
158
|
+
for (const skill of sharedSkills) {
|
|
158
159
|
const dest = path.join(destRoot, skill.name)
|
|
159
|
-
await
|
|
160
|
-
await fs.copy(skill.sourcePath, dest, { overwrite: true, filter: copyFilter(skill.sourcePath) })
|
|
160
|
+
await linkSkill({ skill, dest, backupRoot: path.join(projectDir, '.dwy', 'skill-backups') })
|
|
161
161
|
logAction(`${path.relative(projectDir, destRoot)}/${skill.name}`)
|
|
162
162
|
count++
|
|
163
163
|
}
|
|
@@ -165,3 +165,54 @@ export async function copySkillsToProjectPlatforms({ projectDir, platforms, skil
|
|
|
165
165
|
|
|
166
166
|
return count
|
|
167
167
|
}
|
|
168
|
+
|
|
169
|
+
/** 当前平台实际发现的用户级目录;OpenCode 同时兼容 Claude 与 .agents。 */
|
|
170
|
+
export function globalSkillRoots(platform, homeDir, codexHome = path.join(homeDir, '.codex')) {
|
|
171
|
+
if (platform === 'claude') return [resolveGlobalSkillDir('claude', homeDir)]
|
|
172
|
+
if (platform === 'codex') return [resolveGlobalSkillDir('agents', homeDir), path.join(codexHome, 'skills')]
|
|
173
|
+
if (platform === 'opencode') return ['opencode', 'claude', 'agents'].map(id => resolveGlobalSkillDir(id, homeDir))
|
|
174
|
+
return []
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** 查找有效全局入口;坏链或目录缺少 SKILL.md 时不能作为项目去重依据。 */
|
|
178
|
+
export async function findGlobalSkill({ name, platform, homeDir, codexHome }) {
|
|
179
|
+
for (const root of globalSkillRoots(platform, homeDir, codexHome)) {
|
|
180
|
+
const dest = path.join(root, name)
|
|
181
|
+
const stat = await fs.stat(path.join(dest, 'SKILL.md')).catch(error => {
|
|
182
|
+
if (error.code === 'ENOENT' || error.code === 'ENOTDIR') return null
|
|
183
|
+
throw error
|
|
184
|
+
})
|
|
185
|
+
if (stat?.isFile()) return dest
|
|
186
|
+
}
|
|
187
|
+
return null
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** 按实际发现范围扫描有效全局 Skills,供默认选择使用;覆盖自定义 CODEX_HOME。 */
|
|
191
|
+
export async function scanGlobalSkills({ homeDir, codexHome }) {
|
|
192
|
+
const names = new Set()
|
|
193
|
+
for (const platform of ['claude', 'codex', 'opencode']) {
|
|
194
|
+
for (const root of globalSkillRoots(platform, homeDir, codexHome)) {
|
|
195
|
+
for (const name of await scanExisting(path.dirname(root), 'skills')) {
|
|
196
|
+
if (await findGlobalSkill({ name, platform, homeDir, codexHome })) names.add(name)
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return names
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** 全局优先:已有全局副本迁为共享链接,项目重复真实目录备份后移除,返回去重名称。 */
|
|
204
|
+
export async function deduplicateProjectSkills({ projectDir, platform, skills, homeDir, codexHome }) {
|
|
205
|
+
const projectRoot = resolveProjectSkillDir(projectDir, platform)
|
|
206
|
+
const duplicates = new Set()
|
|
207
|
+
if (!projectRoot || path.resolve(projectDir) === path.resolve(homeDir)) return duplicates
|
|
208
|
+
for (const skill of skills) {
|
|
209
|
+
const globalEntry = await findGlobalSkill({ name: skill.name, platform, homeDir, codexHome })
|
|
210
|
+
if (!globalEntry) continue
|
|
211
|
+
await linkSkill({ skill, dest: globalEntry, backupRoot: path.join(homeDir, '.dwy', 'skill-backups') })
|
|
212
|
+
const dest = path.join(projectRoot, skill.name)
|
|
213
|
+
await removeSkillEntry({ dest, backupRoot: path.join(projectDir, '.dwy', 'skill-backups') })
|
|
214
|
+
duplicates.add(skill.name)
|
|
215
|
+
logAction(`${platform}/${skill.name} 使用全局入口,移除项目重复入口`)
|
|
216
|
+
}
|
|
217
|
+
return duplicates
|
|
218
|
+
}
|
package/src/sync-opencode.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import fs from 'fs-extra'
|
|
2
2
|
import path from 'path'
|
|
3
3
|
import { chalk } from './utils.js'
|
|
4
|
+
import { linkSkill, prepareSharedSkills, removeSkillEntry } from './skill-store.js'
|
|
4
5
|
import {
|
|
5
|
-
copyFilter,
|
|
6
6
|
logAction,
|
|
7
7
|
resolveSourceDir,
|
|
8
8
|
scanExisting,
|
|
@@ -15,8 +15,7 @@ async function copyItemsTo(baseDir, typeDir, items) {
|
|
|
15
15
|
|
|
16
16
|
for (const item of items) {
|
|
17
17
|
const dest = path.join(targetDir, item.name)
|
|
18
|
-
await
|
|
19
|
-
await fs.copy(item.sourcePath, dest, { overwrite: true, filter: copyFilter(item.sourcePath) })
|
|
18
|
+
await linkSkill({ skill: item, dest, backupRoot: path.join(baseDir, '..', '.dwy', 'skill-backups') })
|
|
20
19
|
logAction(`.opencode/${typeDir}/${item.name}`)
|
|
21
20
|
count++
|
|
22
21
|
}
|
|
@@ -31,7 +30,7 @@ async function removeUnselected(typeDir, existingNames, selectedNames, templateN
|
|
|
31
30
|
for (const name of existingNames) {
|
|
32
31
|
if (selectedNames.has(name)) continue
|
|
33
32
|
if (!templateNames.has(name)) continue
|
|
34
|
-
await
|
|
33
|
+
await removeSkillEntry({ dest: path.join(targetDir, name), backupRoot: path.join(baseDir, '..', '.dwy', 'skill-backups') })
|
|
35
34
|
logAction(`.opencode/${typeDir}/${name}`, 'red', '×')
|
|
36
35
|
count++
|
|
37
36
|
}
|
|
@@ -45,6 +44,8 @@ export async function syncOpenCode({
|
|
|
45
44
|
projectDir,
|
|
46
45
|
global = false,
|
|
47
46
|
selected,
|
|
47
|
+
homeDir,
|
|
48
|
+
sharedSkills,
|
|
48
49
|
staleRemovals = {},
|
|
49
50
|
} = {}) {
|
|
50
51
|
const sourceDir = sourceDirOverride || await resolveSourceDir()
|
|
@@ -74,7 +75,10 @@ export async function syncOpenCode({
|
|
|
74
75
|
let syncedCount = 0
|
|
75
76
|
let removedCount = 0
|
|
76
77
|
|
|
77
|
-
|
|
78
|
+
const skills = sharedSkills
|
|
79
|
+
? sharedSkills.filter(skill => selectedNames('skills').has(skill.name))
|
|
80
|
+
: await prepareSharedSkills({ skills: syncedSelection.skills, homeDir })
|
|
81
|
+
syncedCount += await copyItemsTo(openCodeDir, 'skills', skills)
|
|
78
82
|
|
|
79
83
|
removedCount += await removeUnselected(
|
|
80
84
|
'skills',
|
package/src/sync.js
CHANGED
|
@@ -3,6 +3,7 @@ import path from 'path'
|
|
|
3
3
|
import { isCancel } from '@clack/prompts'
|
|
4
4
|
import { tabMultiselect } from './tab-multiselect.js'
|
|
5
5
|
import { chalk, PACKAGE_ROOT } from './utils.js'
|
|
6
|
+
import { linkSkill, prepareSharedSkills, removeSkillEntry } from './skill-store.js'
|
|
6
7
|
|
|
7
8
|
const CATEGORIES = [
|
|
8
9
|
{ key: 'skills', label: 'Skills' },
|
|
@@ -243,8 +244,7 @@ export function logAction(label, color = 'green', prefix = '✓') {
|
|
|
243
244
|
async function copyItems(items, targetDir) {
|
|
244
245
|
for (const item of items) {
|
|
245
246
|
const dest = path.join(targetDir, item.type + 's', item.name)
|
|
246
|
-
await
|
|
247
|
-
await fs.copy(item.sourcePath, dest, { overwrite: true, filter: copyFilter(item.sourcePath) })
|
|
247
|
+
await linkSkill({ skill: item, dest, backupRoot: path.join(targetDir, '..', '.dwy', 'skill-backups') })
|
|
248
248
|
logAction(`${item.type}s/${item.name}`)
|
|
249
249
|
}
|
|
250
250
|
return items.length
|
|
@@ -278,7 +278,11 @@ async function removeUnselected(typePlural, existingNames, selectedNames, templa
|
|
|
278
278
|
// 项目独有(模板未提供过)的条目永不删除,保留用户自定义
|
|
279
279
|
if (!templateNames.has(name)) continue
|
|
280
280
|
const target = path.join(targetDir, typePlural, name)
|
|
281
|
-
|
|
281
|
+
if (typePlural === 'skills') {
|
|
282
|
+
await removeSkillEntry({ dest: target, backupRoot: path.join(targetDir, '..', '.dwy', 'skill-backups') })
|
|
283
|
+
} else {
|
|
284
|
+
await fs.remove(target)
|
|
285
|
+
}
|
|
282
286
|
logAction(`${typePlural}/${name}`, 'red', '×')
|
|
283
287
|
count++
|
|
284
288
|
}
|
|
@@ -400,6 +404,8 @@ export async function syncClaude({
|
|
|
400
404
|
projectDir,
|
|
401
405
|
global = false,
|
|
402
406
|
selected,
|
|
407
|
+
homeDir,
|
|
408
|
+
sharedSkills,
|
|
403
409
|
staleRemovals = {},
|
|
404
410
|
} = {}) {
|
|
405
411
|
const sourceDir = sourceDirOverride || await resolveSourceDir()
|
|
@@ -455,7 +461,10 @@ export async function syncClaude({
|
|
|
455
461
|
let syncedCount = 0
|
|
456
462
|
let removedCount = 0
|
|
457
463
|
|
|
458
|
-
|
|
464
|
+
const skills = sharedSkills
|
|
465
|
+
? sharedSkills.filter(skill => selectedNames('skills').has(skill.name))
|
|
466
|
+
: await prepareSharedSkills({ skills: syncedSelection.skills, homeDir })
|
|
467
|
+
syncedCount += await copyItems(skills, projectTargetDir)
|
|
459
468
|
syncedCount += await syncSettings(sourceDir, projectTargetDir, selectedHookNames, global)
|
|
460
469
|
syncedCount += await copyHooks(syncedSelection.hooks, projectTargetDir)
|
|
461
470
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## 输出完整性要求
|
|
4
4
|
|
|
5
|
-
-
|
|
5
|
+
- **文件路径**:对话回复中使用完整绝对路径,并提供可点击的文件链接;不要只写文件名或相对路径。写入仓库的规则、代码、文档和提交说明仍使用从仓库根开始的完整相对路径,不写本机绝对路径。
|
|
6
6
|
- **服务地址**:写出完整 URL,包含协议、主机、端口和路径(如 `http://localhost:8000/api/users`)。
|
|
7
7
|
- **数据库连接串**:说明它来自哪个环境变量或配置项。回复、日志、示例里不写密码和 token。需要示意时用占位符,如 `postgresql+asyncpg://USER:PASSWORD@localhost:5432/DBNAME`。
|
|
8
8
|
|
|
@@ -52,6 +52,12 @@
|
|
|
52
52
|
|
|
53
53
|
---
|
|
54
54
|
|
|
55
|
+
## 0.32.0 — 2026-10-29
|
|
56
|
+
|
|
57
|
+
### Changed
|
|
58
|
+
- `skills/发布发版/dwy-publish`:按工程识别变更并维护版本、changelog 和 tag;部署新代码前自动补齐并校验三者与部署提交,按项目上下文判断发布方式。
|
|
59
|
+
- `skills/发布发版/dwy-semver`:按版本和发布用途判断正式版或预发布版,不根据分支判断。
|
|
60
|
+
|
|
55
61
|
## 0.31.0 — 2026-10-10
|
|
56
62
|
|
|
57
63
|
### Changed
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.32.0
|
|
@@ -108,8 +108,8 @@ select t.name from t where t.id > 200 limit 50
|
|
|
108
108
|
|
|
109
109
|
```dolphindb
|
|
110
110
|
loadPlugin("aws")
|
|
111
|
-
aws_cfg = dict(["filePath", "region", "bucket", "accessId", "secretKey"], ["demo.csv.gz", 'cn-north-1', 'tests3func', '
|
|
112
|
-
'
|
|
111
|
+
aws_cfg = dict(["filePath", "region", "bucket", "accessId", "secretKey"], ["demo.csv.gz", 'cn-north-1', 'tests3func', 'YOUR_AWS_ACCESS_KEY_ID',
|
|
112
|
+
'YOUR_AWS_SECRET_ACCESS_KEY'])
|
|
113
113
|
t = createExternalTable("test","s3", aws_cfg,`Open`High`OpenInt, [DOUBLE, DOUBLE, INT]);
|
|
114
114
|
select count(*) from t where Open > 0.5 and High < 0.6 limit 1;
|
|
115
115
|
```
|
package/templates/ai-tools/skills/DolphinDB/dwy-dolphindb/references/official/tools/superset.md
CHANGED
|
@@ -60,7 +60,7 @@ Superset 进行可视化数据分析。
|
|
|
60
60
|
`rand(33..126, 32).concat("").base64Encode()`。
|
|
61
61
|
|
|
62
62
|
```dolphindb
|
|
63
|
-
echo "SECRET_KEY = '
|
|
63
|
+
echo "SECRET_KEY = 'YOUR_RANDOM_SUPERSET_SECRET_KEY'" > superset_config.py
|
|
64
64
|
```
|
|
65
65
|
|
|
66
66
|
配置环境变量并初始化 Superset。
|
|
@@ -1,35 +1,28 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: dwy-publish
|
|
3
|
-
description: "
|
|
3
|
+
description: "Use when 用户要求整理工程版本、更新 changelog、打版本 tag、发版、发布或部署上线。"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# 工程版本管理与发布编排
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
先识别工程及变更,再为需要更新的工程维护版本、changelog 和 tag;是否发布及发布方式按用户意图和项目上下文判断。
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
## 流程
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
| 6 | commit 版本变更 + 打 tag 并推送 | 通用步 | `references/git-tag.md` |
|
|
20
|
-
| 7 | build + 触发发布 | 项目步 | `references/<平台>.md`;走 GA 引 `../dwy-github-action-publish` |
|
|
21
|
-
| 8 | 监控发布进程 | 项目步 | `references/monitor-notify.md` |
|
|
22
|
-
| 9 | 通知结果(输出结果文本) | — | `references/monitor-notify.md` |
|
|
12
|
+
1. 读取仓库指令、结构、构建配置、版本来源、changelog、Git 状态和历史 tag,识别独立工程及共享版本边界。依据实际依赖判断共享代码影响;不要把每个子目录当成独立工程。优先遵循用户指定范围,分不清边界时询问。
|
|
13
|
+
2. 逐工程列出变更、版本来源、changelog 路径、tag 命名、版本类型、是否需要发布和 tag 触发器。检查上次对应 tag 后的提交及本次相关的未提交变更;不要把不属于本次的改动纳入提交。
|
|
14
|
+
3. 对本次需要更新的工程,按 `../dwy-semver` 确定版本级别、beta/rc 或正式版,按 `references/version-sync.md` 同步版本;按 `references/changelog.md` 更新该工程 changelog,每个版本条目必须包含明确版本号和日期,禁止使用 `Unreleased` 或“未发布”等占位标题,与是否发布无关;再按 `references/git-tag.md` 提交和打 tag。无变更工程不创建空版本。已覆盖本次代码的现有版本可复用,不重复升版或打 tag。
|
|
15
|
+
4. 确定是否发布:仅版本管理时结束;需要发布或部署时,读取项目约定及现有 CI、构建和部署配置,判断渠道和命令。遵守仓库的发布和授权规则,不套用固定发布方式;证据不足时询问。不根据分支判断版本类型或发布流程。
|
|
16
|
+
5. **部署上线闸门**:有新代码要上线,AI 自动检查并补齐对应工程的版本更新、changelog 和 tag,不逐项要求用户确认。自动校验 tag 指向包含全部拟部署代码、版本和 changelog 的提交,构建或部署使用该提交或其已验证产物。补齐并校验通过后继续部署;无法补齐或校验失败时停止部署并说明原因,不能以赶时间或已有构建替代。先完成项目要求的测试和适用的安全检查;SDK 对外发布前读取 `../dwy-sdk-spec`。
|
|
17
|
+
6. 推送 tag 前检查远端工作流及其他自动触发器:若推送会发布,先完成所有发布闸门并确认本次已获发布授权。仅要求版本管理而推送会触发未经授权的发布时,保留本地 tag 并报告,不能推送或擅自更改 workflow。
|
|
18
|
+
7. 已获授权且满足闸门时,按项目实际配置执行发布;需要 GitHub Actions 打包时读取 `../dwy-github-action-publish` 和相应平台参考文件。按 `references/monitor-notify.md` 监控并验证实际结果。
|
|
23
19
|
|
|
24
|
-
|
|
20
|
+
仅重发、重跑或回滚**完全相同的已记录版本和产物**,可复用对应 changelog、版本及 tag;必须核实代码和产物身份。有新代码则不是重发,必须完成新版本的全部闸门。仅环境配置变更按项目约定记录和验证,不得借此夹带新代码。
|
|
25
21
|
|
|
26
|
-
##
|
|
22
|
+
## 参考文件
|
|
27
23
|
|
|
28
|
-
-
|
|
29
|
-
-
|
|
30
|
-
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
- 全程 AI 自动判断、自动跑,仅搞不定时才问用户(版本级别拿不准、命令探测不到、配置缺失、构建/发布失败、需删远端 tag)。用 `develop` 部署正式环境时除外,须先问用户。
|
|
34
|
-
- 编排时按文件路径主动加载子 skill(`../dwy-semver`、`../dwy-sdk-spec`、`../dwy-github-action-publish`);这些 skill 也可被用户独立触发。
|
|
35
|
-
- `.dwy/` 缓存提醒用户加进 `.gitignore`。
|
|
24
|
+
- 版本和记录:`references/version-sync.md`、`references/changelog.md`、`references/git-tag.md`。
|
|
25
|
+
- 发布配置:仅需要发布时读取 `references/publish-config.md`,必要时使用 `bash scripts/release_config.sh`。
|
|
26
|
+
- 平台:`references/frontend-deploy.md`、`references/backend-deploy.md`、`references/ios.md`、`references/android.md`、`references/harmony.md`、`references/generic-release.md`。
|
|
27
|
+
|
|
28
|
+
版本、tag 或发布目标存在歧义时先核实;不可识别时询问。删除或覆盖远端 tag / release 前必须获得用户明确授权。输出各工程的版本、changelog、tag、是否推送和实际发布验证结果。
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
## 版本(通用步,固化)
|
|
11
11
|
|
|
12
12
|
- `versionName` = 语义版本号,走 `../dwy-semver` 决策 + bump.py。
|
|
13
|
-
- `versionCode`
|
|
13
|
+
- 新上传构建的 `versionCode` 遵循渠道及项目的递增要求。
|
|
14
14
|
- 多文件同步见 `version-sync.md`。
|
|
15
15
|
|
|
16
16
|
## build / publish(项目步,不写死命令)
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
|
|
27
27
|
## changelog / tag / 安全检查 / 监控
|
|
28
28
|
|
|
29
|
-
- changelog → `changelog.md
|
|
30
|
-
- tag → `git-tag.md`(每次 bump 必打;版本串与 changelog
|
|
29
|
+
- changelog → `changelog.md`(使用该工程既有路径和格式)
|
|
30
|
+
- tag → `git-tag.md`(每次 bump 必打;版本串与 changelog 标题一致)
|
|
31
31
|
- 安全检查 → `../dwy-sdk-spec`
|
|
32
32
|
- 监控 + 结果 → `monitor-notify.md`
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
## 版本(通用步,固化)
|
|
6
6
|
|
|
7
|
-
- 走 `../dwy-semver` 决策 + bump.py
|
|
8
|
-
-
|
|
7
|
+
- 走 `../dwy-semver` 决策 + bump.py;按 `version-sync.md` 同步工程实际维护的版本来源。
|
|
8
|
+
- 部署新代码必须先完成入口规定的版本、changelog 和 tag 闸门;仅重发或回滚相同已记录产物可复用原版本。
|
|
9
9
|
|
|
10
10
|
## build / deploy(项目步,不写死命令)
|
|
11
11
|
|
|
@@ -19,6 +19,6 @@
|
|
|
19
19
|
|
|
20
20
|
## changelog / tag / 监控
|
|
21
21
|
|
|
22
|
-
- changelog → `changelog.md
|
|
23
|
-
- tag → `git-tag.md`(每次 bump 必打;版本串与 changelog
|
|
22
|
+
- changelog → `changelog.md`(使用该工程既有路径和格式)
|
|
23
|
+
- tag → `git-tag.md`(每次 bump 必打;版本串与 changelog 标题一致)
|
|
24
24
|
- 监控 + verify → `monitor-notify.md`(后端信号:健康检查 / 版本接口 / 运行日志 / 镜像 tag)
|
|
@@ -1,75 +1,11 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 按工程维护 CHANGELOG
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
先识别版本边界:根据构建配置、版本来源、已有 changelog、tag 和项目约定确定独立工程;共享版本的模块遵循仓库既有规则。用户指定范围优先,不将无关改动纳入本次版本。
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
逐工程检查上次对应 tag 后的提交和本次相关未提交变更,分析共享代码对各工程的影响。列出工程、变更、版本来源、changelog 路径、tag 和是否发布。无变更的工程不写空版本;已记录的内容不要重复添加。
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
需要更新的工程各自维护 changelog:优先使用现有路径、分类和语言;没有时在工程目录创建 `CHANGELOG.md`,遵循仓库约定。记录该工程本次实际变更,不混入其他工程。用户要求仅管理版本时也要记录,不以是否发布为条件。
|
|
8
8
|
|
|
9
|
-
1.
|
|
10
|
-
2. 一端算存在:该目录有自己的版本来源(前端看 `package.json` 的 `version`,后端看 `pyproject.toml` 的 `version`),或有该端的应用入口。没有这一端就不写。
|
|
11
|
-
3. 决定哪些端要写:
|
|
12
|
-
- 用户点名了项目或端:只写点名的。
|
|
13
|
-
- 没点名:该端目录自上次 release tag 以来有提交才写。没有上次 tag 时,看这次准备发布的提交是否改过该目录。
|
|
9
|
+
每个版本条目的标题必须同时包含明确版本号和 `YYYY-MM-DD` 日期,例如 `## [1.2.3] - 2026-11-02`;版本串与该工程版本来源及 tag 中的版本串一致。日期表示本次版本记录日期,不以实际发布为前提,也不等到发布时再补。禁止使用 `Unreleased`、`[Unreleased]` 或“未发布”等占位标题;即使仓库原格式缺少版本号或日期,本次新增或更新的条目也必须补齐。已有占位条目中属于本次的变更,归入明确版本号和日期的条目,不继续保留为占位记录。按现有分类和语言写清用户可理解的变更;仓库已有可靠生成工具时可复用并检查结果。
|
|
14
10
|
|
|
15
|
-
|
|
16
|
-
git log <该端上次 tag>..HEAD --oneline -- <项目>/<端>/
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
4. 没有提交的端不写、不升版本、不打 tag。
|
|
20
|
-
5. 写之前先列出清单:项目、端、changelog 路径、有没有变更。清单里该写的还没写完,不进入 tag。
|
|
21
|
-
|
|
22
|
-
该项目下若还有 android / ios / harmony,同样各算一端,不写进前后端的 changelog。
|
|
23
|
-
|
|
24
|
-
## 落点
|
|
25
|
-
|
|
26
|
-
- `<项目>/frontend/CHANGELOG.md`
|
|
27
|
-
- `<项目>/backend/CHANGELOG.md`
|
|
28
|
-
|
|
29
|
-
只有一端的项目只写存在的那一端。不要在工厂根再写一份总表。
|
|
30
|
-
|
|
31
|
-
## 版本标题格式(与 git tag 对齐)
|
|
32
|
-
|
|
33
|
-
新版本必须用二级标题,版本串单独成标题,供打 tag 原样取用:
|
|
34
|
-
|
|
35
|
-
```markdown
|
|
36
|
-
# <包标识> ← 作为 tag 的包前缀(如 shop-frontend、@acme/api)
|
|
37
|
-
|
|
38
|
-
## 0.16.1 ← 新版本标题;这一份 changelog 的 tag 版本串必须与此完全一致
|
|
39
|
-
|
|
40
|
-
### Patch Changes
|
|
41
|
-
|
|
42
|
-
- ...
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
约束:
|
|
46
|
-
|
|
47
|
-
- 标题里只写 SemVer 版本串,不要写 `v0.16.1`、日期或其它后缀
|
|
48
|
-
- 从 `develop` 发:标题是 `x.y.z-beta.N` 或 `x.y.z-rc.N`。从 `preview` 发正式版:标题是 `x.y.z`,不带预发布
|
|
49
|
-
- 该版本串 = 这一端版本来源文件里的 version = 这一份 changelog 即将打的 tag 中的版本部分(见 `git-tag.md`)
|
|
50
|
-
- 各端版本号各自维护。前端升了,不表示后端也要升
|
|
51
|
-
|
|
52
|
-
## 写法
|
|
53
|
-
|
|
54
|
-
1. 找这一端的上一个 release tag
|
|
55
|
-
2. 只取这一端目录里的 commit(命令见上)
|
|
56
|
-
3. 按 `feat` / `fix` / `refactor` / `chore` 分组
|
|
57
|
-
4. 写入这一端的 `CHANGELOG.md`,用 `## <新版本号>` 标注
|
|
58
|
-
|
|
59
|
-
仓库已有自动生成命令(changelogen 等)时优先使用。仍然只覆盖这一端的目录,版本标题仍要满足上面的格式。
|
|
60
|
-
|
|
61
|
-
写了几份 changelog,就按 `git-tag.md` 各打一个 tag。不要合成一个工厂级 tag。
|
|
62
|
-
|
|
63
|
-
## 自动执行
|
|
64
|
-
|
|
65
|
-
changelog 由 AI 生成并写入,不逐步确认。只有 commit 无法归类,或分不清项目边界时才问用户。
|
|
66
|
-
|
|
67
|
-
## 禁止
|
|
68
|
-
|
|
69
|
-
- 禁止在工厂根写一份总 CHANGELOG,把多个项目或前端后端混在一起
|
|
70
|
-
- 禁止只写了其中一个项目就进入 tag
|
|
71
|
-
- 禁止把 A 项目的 commit 写进 B 项目
|
|
72
|
-
- 禁止把前端的 commit 写进后端,或反过来
|
|
73
|
-
- 禁止给没有变更的端写一份空版本
|
|
74
|
-
- 禁止 changelog 未写完就进入 tag / 部署
|
|
75
|
-
- 禁止版本标题与这一份即将打的 tag 版本串不一致
|
|
11
|
+
部署新代码前 AI 自动检查目标工程的 changelog 是否覆盖拟部署变更,并与版本和 tag 指向的提交一致;缺失时自动补齐,不逐项要求用户确认。补齐和校验完成前禁止部署。
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
## 版本(通用步,固化)
|
|
6
6
|
|
|
7
7
|
- 走 `../dwy-semver` 决策 + bump.py;多文件同步见 `version-sync.md`(前端改 `package.json` 的 `version`)。
|
|
8
|
-
-
|
|
8
|
+
- 部署新代码必须先完成入口规定的版本、changelog 和 tag 闸门;仅重发或回滚相同已记录产物可复用原版本。
|
|
9
9
|
|
|
10
10
|
## build / deploy(项目步,不写死命令)
|
|
11
11
|
|
|
@@ -19,6 +19,6 @@
|
|
|
19
19
|
|
|
20
20
|
## changelog / tag / 监控
|
|
21
21
|
|
|
22
|
-
- changelog → `changelog.md
|
|
23
|
-
- tag → `git-tag.md`(每次 bump 必打;版本串与 changelog
|
|
22
|
+
- changelog → `changelog.md`(使用该工程既有路径和格式)
|
|
23
|
+
- tag → `git-tag.md`(每次 bump 必打;版本串与 changelog 标题一致)
|
|
24
24
|
- 监控 + verify → `monitor-notify.md`(前端信号:页面访问 / 版本接口 / 产物 hash / CDN 刷新)
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
## 版本(通用步,固化)
|
|
6
6
|
|
|
7
|
-
- 走 `../dwy-semver` 决策 + bump.py
|
|
8
|
-
-
|
|
7
|
+
- 走 `../dwy-semver` 决策 + bump.py;按 `version-sync.md` 同步工程实际维护的版本来源。
|
|
8
|
+
- 发布新代码必须先完成入口规定的版本、changelog 和 tag 闸门;仅重发或回滚相同已记录产物可复用原版本。
|
|
9
9
|
|
|
10
10
|
## build / publish(项目步,不写死命令)
|
|
11
11
|
|
|
@@ -16,13 +16,13 @@
|
|
|
16
16
|
按缓存 method 分流:
|
|
17
17
|
- `github-action-oidc` → **PyPI/npm 首选**,引 `../dwy-github-action-publish`(OIDC + Trusted Publisher)
|
|
18
18
|
- `github-action` → Cython wheel / Electron / 镜像,引 `../dwy-github-action-publish`
|
|
19
|
-
-
|
|
19
|
+
- 其他渠道 → 遵循项目已有发布配置和授权;PyPI / npm 发布必须走 GitHub Actions OIDC。
|
|
20
20
|
|
|
21
21
|
**PyPI/npm 未配置 GA 时按 `publish-config.md` 主动推荐 OIDC。**
|
|
22
22
|
|
|
23
23
|
## changelog / tag / 安全检查 / 监控
|
|
24
24
|
|
|
25
|
-
- changelog → `changelog.md
|
|
26
|
-
- tag → `git-tag.md`(每次 bump 必打;版本串与 changelog
|
|
25
|
+
- changelog → `changelog.md`(使用该工程既有路径和格式)
|
|
26
|
+
- tag → `git-tag.md`(每次 bump 必打;版本串与 changelog 标题一致;monorepo 例:`@dwydev/eui@2.2.3`、`create-dwy@0.16.1`)
|
|
27
27
|
- 安全检查 → `../dwy-sdk-spec`(对外包必查泄露/产物)
|
|
28
28
|
- 监控 + verify → `monitor-notify.md`(包仓库版本查询 / GitHub Release)
|
|
@@ -1,82 +1,7 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 版本 tag
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
每个需要更新版本的工程,在版本来源和 changelog 完成后提交对应变更并打 tag。tag 的版本串必须与版本来源和 changelog 标题一致;tag 指向包含本次完整代码、版本和 changelog 的提交。遵循仓库既有命名,多个独立工程使用各自不冲突的 tag,已有约定优先。
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
打 tag 和推送前检查 tag 是否已存在、指向是否正确,以及推送是否触发 CI、发布或部署。推送由用户授权范围和项目要求决定;推送可能触发发布时,先完成测试、适用的安全检查及部署闸门,且仅在发布已获授权时推送。仅版本管理且推送会触发未经授权发布时,保留本地 tag 并报告。
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
1. 将版本来源文件 + CHANGELOG 一并 commit(若尚未提交)
|
|
10
|
-
2. 按下方规则算出 tag 名
|
|
11
|
-
3. 本地打 tag → push tag 到 origin
|
|
12
|
-
4. 再进入 build / 发布(若 workflow 由 tag 触发,push tag 即触发发布)
|
|
13
|
-
|
|
14
|
-
```bash
|
|
15
|
-
# 版本与 changelog 已落盘并 commit 后
|
|
16
|
-
git tag <tag>
|
|
17
|
-
git push origin <tag>
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
## tag 与 changelog 版本对齐(硬规则)
|
|
21
|
-
|
|
22
|
-
tag 中的**版本串**必须与这一份 CHANGELOG **新版本标题**完全一致,禁止改写。第 4 步写了几份,就各打一个 tag,版本串对应该份标题。
|
|
23
|
-
|
|
24
|
-
| CHANGELOG 写法 | 取出的版本串 |
|
|
25
|
-
|----------------|-------------|
|
|
26
|
-
| `## 0.16.1` | `0.16.1` |
|
|
27
|
-
| `## 2.2.3` | `2.2.3` |
|
|
28
|
-
| `## 1.0.0-rc.1` | `1.0.0-rc.1` |
|
|
29
|
-
|
|
30
|
-
禁止:
|
|
31
|
-
|
|
32
|
-
- changelog 写 `## 0.16.1`,tag 打成 `v0.16.1` / `0.16.1.0` / `0.16`(格式不一致)
|
|
33
|
-
- tag 版本与 `package.json` / `pyproject.toml` 等来源文件不一致
|
|
34
|
-
- 未写 changelog 新版本标题就打 tag
|
|
35
|
-
|
|
36
|
-
## tag 全名拼法
|
|
37
|
-
|
|
38
|
-
先取 changelog 版本串 `VER`,再按仓库惯例拼全名:
|
|
39
|
-
|
|
40
|
-
| 场景 | 规则 | 例 |
|
|
41
|
-
|------|------|-----|
|
|
42
|
-
| monorepo 多包 / 多平台 | `{包标识}@{VER}`;包标识优先取该 CHANGELOG 顶部 H1(去掉 `# `),否则取 `package.json` 的 `name` | CHANGELOG H1 `# create-dwy` + `## 0.16.1` → `create-dwy@0.16.1`;H1 `# @dwydev/eui` + `## 2.2.3` → `@dwydev/eui@2.2.3` |
|
|
43
|
-
| 单包仓库且历史 tag 已有固定前缀 | 沿用历史前缀 + `VER`(仅当前缀不篡改 VER 本身时) | 历史 `v1.2.0` 且 changelog 为 `## 1.3.0` 时,若项目约定 `v` 前缀且 VER 仍完整出现 → 须在本仓库已有 tag 惯例中确认;**优先保证 VER 与 changelog 一致** |
|
|
44
|
-
| 单包仓库无历史惯例 | 直接用 `VER` | changelog `## 1.0.0` → tag `1.0.0` |
|
|
45
|
-
|
|
46
|
-
**冲突裁决**:若「历史 `v` 前缀」与「changelog 无 `v`」冲突,以 **changelog 标题里的版本串** 为准拼 tag(monorepo 用 `包@VER`,单包用 `VER`),不要为了凑 `v` 前缀改掉 VER。仓库若已有 workflow 写死 `v*` 触发,同步改 workflow 或 changelog 标题约定,二者必须一致。
|
|
47
|
-
|
|
48
|
-
打 tag 前自检:
|
|
49
|
-
|
|
50
|
-
```bash
|
|
51
|
-
# VER 必须等于 changelog 最新 ## 标题
|
|
52
|
-
# 包来源文件 version 字段必须等于 VER
|
|
53
|
-
git tag -l | head # 对照历史命名是否 monorepo 包前缀
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
## 推送与触发
|
|
57
|
-
|
|
58
|
-
- 必须 `git push origin <tag>`,只打本地 tag 不算完成
|
|
59
|
-
- method 为 `github-action` / `github-action-oidc` 且 workflow `on.push.tags` 时:push tag 即触发 CI,不必再另起发布命令
|
|
60
|
-
- method 为 local / testflight / maven 等:先 push tag,再跑缓存的 `publish_cmd`(或按项目既有顺序,但 tag 不可省)
|
|
61
|
-
|
|
62
|
-
## 重发 / 删 tag
|
|
63
|
-
|
|
64
|
-
远端已有错误 tag 需要覆盖时:
|
|
65
|
-
|
|
66
|
-
1. **先确认用户允许**删除远端 tag / release
|
|
67
|
-
2. 再删远端 + 本地,重打同名 tag 推送
|
|
68
|
-
|
|
69
|
-
```bash
|
|
70
|
-
git push origin :refs/tags/<tag>
|
|
71
|
-
git tag -d <tag>
|
|
72
|
-
git tag <tag>
|
|
73
|
-
git push origin <tag>
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
## 禁止
|
|
77
|
-
|
|
78
|
-
- 禁止版本号已 bump 却不打 tag
|
|
79
|
-
- 禁止 tag 版本串与 changelog 新版本标题不一致
|
|
80
|
-
- 禁止 tag 与版本来源文件不一致
|
|
81
|
-
- 禁止未 push 远端就当发版完成
|
|
82
|
-
- 禁止未确认用户就删除远端已有 tag / release
|
|
7
|
+
部署新代码必须使用包含该代码的版本 tag 对应提交或其已验证产物;不能用旧 tag 掩盖新增代码。重发或回滚完全相同的已记录版本和产物,可复用原 tag,无须重复打 tag。不得未经用户明确授权删除或覆盖远端 tag / release。
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
|
|
21
21
|
## changelog / tag / 安全检查 / 监控
|
|
22
22
|
|
|
23
|
-
- changelog → `changelog.md
|
|
24
|
-
- tag → `git-tag.md`(每次 bump 必打;版本串与 changelog
|
|
23
|
+
- changelog → `changelog.md`(使用该工程既有路径和格式)
|
|
24
|
+
- tag → `git-tag.md`(每次 bump 必打;版本串与 changelog 标题一致)
|
|
25
25
|
- 安全检查 → `../dwy-sdk-spec`
|
|
26
26
|
- 监控 + 结果 → `monitor-notify.md`
|