@ranger1/dx 0.1.64 → 0.1.66

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.
@@ -0,0 +1,122 @@
1
+ ---
2
+ name: backend-artifact-deploy
3
+ description: 将后端部署从“目标机拉源码并编译”改造为“本地构建制品、目标机仅安装运行依赖并启动”的标准流程。用于 Node/NestJS/Nx/Prisma 等后端项目,尤其适合需要无源码部署、支持 dev/staging/prod 多环境、要求双层环境文件覆盖(如 .env.production 与 .env.production.local)、以及需要在 pm2 与 direct 启动方式之间切换的场景。
4
+ ---
5
+
6
+ # 后端制品部署
7
+
8
+ ## 概览
9
+
10
+ 使用该技能时,先识别项目当前的环境变量加载链路与启动链路,再落地“制品打包脚本 + 服务器发布脚本 + 回滚策略”。
11
+ 目标是保证目标机器不需要源码编译,同时保持与项目既有环境覆盖规则一致。
12
+
13
+ ## 执行流程
14
+
15
+ ### 第一步:确认现状链路
16
+
17
+ 依次核对:
18
+
19
+ 1. 构建链路是否依赖源码目录(例如 dist 里软链回源码 `node_modules`)。
20
+ 2. 运行链路如何加载环境变量(是否是两层覆盖,是否通过 `dotenv -e A -e B`)。
21
+ 3. 数据库迁移链路是否依赖运行时环境(Prisma `generate` / `migrate deploy` 前是否已加载 env)。
22
+ 4. 进程启动是否仅支持 pm2,是否需要 direct 前台测试模式。
23
+
24
+ 如果项目已有统一入口(例如 `dx` 或内部脚手架),优先复用该入口,不要绕开既有环境策略。
25
+
26
+ ### 第二步:定义制品边界
27
+
28
+ 默认采用“轻制品”模式:
29
+
30
+ 1. 本地只打包编译产物与必要运行文件,不打包 `node_modules`。
31
+ 2. 目标机解压后再安装生产依赖。
32
+ 3. 制品命名固定含版本与时间片,例如 `backend-v<version>-<月-日-时-分>.tgz`。
33
+
34
+ 制品最小清单应包含:
35
+
36
+ 1. 编译产物目录(如 `dist/backend/**`)。
37
+ 2. 数据库 schema 与迁移目录(如 `prisma/schema/**`)。
38
+ 3. 生产依赖清单(`package.production.json` 重命名为 `package.json`)。
39
+ 4. 锁文件(`pnpm-lock.yaml`)。
40
+ 5. 启动配置(如 `ecosystem.config.cjs`)。
41
+ 6. 双层环境文件(`.env.<env>` 与 `.env.<env>.local`)。
42
+
43
+ ### 第三步:实现打包脚本
44
+
45
+ 打包脚本应支持参数:
46
+
47
+ 1. `--env dev|staging|prod`。
48
+ 2. `--version`(默认取后端 `package.json` 版本)。
49
+ 3. `--time`(格式 `MM-DD-HH-mm`)。
50
+
51
+ 脚本关键行为:
52
+
53
+ 1. 按环境构建(`dev -> --dev`,`staging/prod -> --prod`)。
54
+ 2. 复制双层环境文件到制品目录。
55
+ 3. 不在本地安装运行依赖。
56
+ 4. 生成 `tgz`。
57
+
58
+ ### 第四步:实现发布脚本
59
+
60
+ 发布脚本应支持参数:
61
+
62
+ 1. `--archive`(必填)。
63
+ 2. `--env dev|staging|prod`。
64
+ 3. `--start-mode pm2|direct`(默认 `pm2`)。
65
+ 4. `--env-file` 与 `--env-local-file`(可选覆盖路径)。
66
+ 5. `--skip-install`、`--skip-migration`、`--skip-pm2`。
67
+
68
+ 发布顺序建议:
69
+
70
+ 1. 解压到 `releases/<version>`。
71
+ 2. 准备双层 env 文件。
72
+ 3. 安装生产依赖。
73
+ 4. 执行 `prisma generate`。
74
+ 5. 执行 `prisma migrate deploy`。
75
+ 6. 切换 `current` 软链。
76
+ 7. 启动服务(pm2 或 direct)。
77
+ 8. 清理旧版本。
78
+
79
+ ### 第五步:双层环境加载规则(必须一致)
80
+
81
+ 所有关键步骤统一使用相同加载顺序:
82
+
83
+ 1. 基础层 `.env.<env>`。
84
+ 2. 覆盖层 `.env.<env>.local`。
85
+
86
+ 推荐显式写法:
87
+
88
+ ```bash
89
+ APP_ENV="<env-name>" pnpm exec dotenv -e ".env.<env-name>" -e ".env.<env-name>.local" -- <command>
90
+ ```
91
+
92
+ 命令示例中的 `<command>` 包括:
93
+
94
+ 1. `pnpm exec prisma generate --schema=...`
95
+ 2. `pnpm exec prisma migrate deploy --schema=...`
96
+ 3. `pm2 startOrReload ...` 或 `node apps/backend/src/main.js`
97
+
98
+ ## 验证清单
99
+
100
+ 交付前必须至少验证:
101
+
102
+ 1. 打包脚本 `--help` 与语法检查通过。
103
+ 2. 发布脚本 `--help` 与语法检查通过。
104
+ 3. 制品内同时包含 `.env.<env>` 与 `.env.<env>.local`。
105
+ 4. 发布脚本在默认路径下能正确识别并使用两层 env。
106
+ 5. `start-mode=direct` 可前台启动。
107
+ 6. `start-mode=pm2` 可重载或启动。
108
+ 7. 版本目录与 `current` 切换正常,可回滚。
109
+
110
+ ## 常见陷阱
111
+
112
+ 1. 把 env 文件链接到自身,造成坏链路。
113
+ 2. 只加载 `.env.<env>`,遗漏 `.local` 覆盖。
114
+ 3. 迁移与启动阶段用不同 env 加载逻辑,导致行为不一致。
115
+ 4. staging 构建误用 development 或 production 的 env 层。
116
+ 5. 打包包含本机 `node_modules`,跨系统运行失败。
117
+
118
+ ## 参考资料
119
+
120
+ 需要细化实现时,读取:
121
+
122
+ - `references/deployment-checklist.md`
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "后端制品部署"
3
+ short_description: "跨项目复用的后端制品构建发布与双层环境变量合并流程"
4
+ default_prompt: "Use $backend-artifact-deploy to design and implement source-free backend artifact deployment with layered env handling."
@@ -0,0 +1,66 @@
1
+ # 后端制品部署检查清单
2
+
3
+ ## 一、改造前采样
4
+
5
+ 1. 查找构建后是否存在软链回源码依赖:
6
+
7
+ ```bash
8
+ rg -n "ln -sfn.*node_modules|node_modules.*ln -sfn" <backend-package-json-path>
9
+ ```
10
+
11
+ 2. 查找环境加载入口:
12
+
13
+ ```bash
14
+ rg -n "dotenv -e|dotenv --override|ConfigModule.forRoot|loadEnvironment|env-layers" -S <repo-root>
15
+ ```
16
+
17
+ 3. 查找启动命令:
18
+
19
+ ```bash
20
+ rg -n "start:prod|pm2|node .*main" -S <repo-root>
21
+ ```
22
+
23
+ ## 二、打包脚本最低要求
24
+
25
+ 1. 接收 `--env`、`--version`、`--time`。
26
+ 2. 制品名包含版本与时间片。
27
+ 3. 打入 `.env.<env>` 与 `.env.<env>.local`。
28
+ 4. 不打入 `node_modules`(轻制品模式)。
29
+
30
+ ## 三、发布脚本最低要求
31
+
32
+ 1. 解压到 `releases/<version>` 并切换 `current`。
33
+ 2. 支持 `pm2` 与 `direct` 两种启动方式。
34
+ 3. 在 install、migrate、start 三阶段都用同一套双层 env 加载顺序。
35
+ 4. 支持 `--env-file` 与 `--env-local-file` 覆盖路径。
36
+
37
+ ## 四、上线前验证命令
38
+
39
+ ```bash
40
+ bash -n scripts/release/backend-build-release.sh
41
+ bash -n scripts/release/backend-deploy-release.sh
42
+ scripts/release/backend-build-release.sh --env staging
43
+ tar -tzf release/backend/*.tgz | rg "\.env\.staging(\.local)?$"
44
+ ```
45
+
46
+ ## 五、发布后验证
47
+
48
+ 1. 进程检查:
49
+
50
+ ```bash
51
+ pm2 status
52
+ pm2 logs backend --lines 120
53
+ ```
54
+
55
+ 2. 健康检查:
56
+
57
+ ```bash
58
+ curl -f http://127.0.0.1:3000/health
59
+ ```
60
+
61
+ 3. 回滚检查:
62
+
63
+ ```bash
64
+ ln -sfn /opt/ai-backend/releases/<old-version> /opt/ai-backend/current
65
+ pm2 reload backend --update-env
66
+ ```
@@ -0,0 +1,54 @@
1
+ ---
2
+ name: gh-dependabot-cleanup
3
+ description: Use when a user asks to check or remediate GitHub Dependabot alerts and deliver one closed-loop PR, including alert triage, dependency updates, verification, and PR creation with unresolved items clearly documented.
4
+ ---
5
+
6
+ # GH Dependabot Cleanup
7
+
8
+ ## Overview
9
+ Use this skill to complete a Dependabot remediation loop with minimal manual input. Rely on `gh` directly, fix all patchable alerts in scope, and document unpatched alerts without auto-dismiss.
10
+
11
+ ## Closed Loop Workflow
12
+ 1. Confirm target and scope in one sentence.
13
+ 2. Fetch open alerts with `gh api`.
14
+ 3. Classify alerts into:
15
+ - patchable: has `first_patched_version`
16
+ - unpatched: no `first_patched_version`
17
+ - direct vs transitive
18
+ 4. Announce remediation plan before edits.
19
+ 5. Apply dependency changes (prefer overrides/resolutions for transitive alerts).
20
+ 6. Refresh lockfile.
21
+ 7. Run required project verification commands.
22
+ 8. Commit, push, and open exactly one PR.
23
+ 9. Report residual risk (unpatched alerts) in PR and final reply.
24
+
25
+ ## Default Commands
26
+ ```bash
27
+ # 1) Fetch open alerts JSON
28
+ gh api -H 'Accept: application/vnd.github+json' \
29
+ '/repos/OWNER/REPO/dependabot/alerts?state=open&per_page=100'
30
+
31
+ # 2) Tabular triage view
32
+ gh api -H 'Accept: application/vnd.github+json' \
33
+ '/repos/OWNER/REPO/dependabot/alerts?state=open&per_page=100' \
34
+ | jq -r '.[] | [.number, .security_vulnerability.package.name, .dependency.relationship, .security_advisory.severity, (.security_vulnerability.first_patched_version.identifier // "none"), .security_vulnerability.vulnerable_version_range, .html_url] | @tsv'
35
+
36
+ # 3) Typical lock refresh (adapt to repo)
37
+ pnpm install --lockfile-only
38
+ ```
39
+
40
+ ## Decision Rules
41
+ - Keep unpatched alerts open by default; do not dismiss unless explicitly requested.
42
+ - If one package maps to multiple alerts, upgrade once to the highest required safe version.
43
+ - Keep the PR focused on security dependency remediation only.
44
+ - If repo has branch/issue conventions, follow them strictly.
45
+
46
+ ## PR Requirements
47
+ Include these sections:
48
+ 1. Fixed alerts: alert id, package, target version
49
+ 2. Remaining alerts: alert id, reason (for example, no upstream patch)
50
+ 3. Verification: exact commands run and outcomes
51
+ 4. Risk note: what is deferred and why
52
+
53
+ ## Fast Trigger
54
+ 修复安全警告
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "Dependabot Cleanup PR"
3
+ short_description: "Fix GitHub Dependabot alerts in one PR workflow"
4
+ default_prompt: "Use $gh-dependabot-cleanup to remediate open Dependabot alerts and create one PR."
@@ -0,0 +1,208 @@
1
+ ---
2
+ name: git-commit-and-pr
3
+ description: 在 Git 仓库中执行自动化 Issue、Commit、PR 工作流。用于以下场景:需要零输入一键完成 Issue→分支→Commit→PR;需要根据仓库状态自动判断下一步;需要创建结构化 GitHub Issue;需要基于暂存变更生成规范 commit;需要推送分支并创建 Pull Request;需要串联输出清晰阶段结果。支持仅建 Issue、仅建 PR、指定 Issue 编号、指定 PR 基准分支。
4
+ ---
5
+
6
+ # Git Commit And PR
7
+
8
+ ## 目标
9
+
10
+ 在最少人工输入下完成 `Issue -> Branch -> Commit -> PR` 标准流程,并保持可审计输出。
11
+
12
+ ## 默认行为(零输入)
13
+
14
+ 当用户只调用 `/git-commit-and-pr` 且不带参数时,执行“全自动模式”,不再向用户追问:
15
+
16
+ 1. 检查仓库状态与当前分支。
17
+ 2. 若工作树有未提交改动:执行 Issue -> Branch -> Commit -> PR。
18
+ 3. 若工作树干净:对比当前分支与 `main`(或默认基准)差异,并检查最近提交,判断是否应直接创建 PR。
19
+ 4. 若工作树干净且存在分支差异:直接推送并创建 PR(不再创建新 Issue/Commit)。
20
+ 5. 输出 Issue、Commit、PR 链接与下一步命令。
21
+
22
+ 说明:保留所有旧能力,不影响 `--issue`、`--issue-only`、`--pr --base`。
23
+
24
+ ## 执行原则
25
+
26
+ - 全程中文输出。
27
+ - 先检查状态,再决定执行阶段。
28
+ - 禁止在 `main/master` 直接提交代码。
29
+ - 优先自动完成,不因“小缺信息”中断;只在不可执行时报阻塞。
30
+ - 输出必须包含:已完成项、阻塞原因、下一步建议。
31
+
32
+ ## 输入形式
33
+
34
+ - 自动模式:`/git-commit-and-pr`
35
+ - 指定 Issue:`/git-commit-and-pr --issue <ID>`
36
+ - 仅创建 Issue:`/git-commit-and-pr --issue-only`
37
+ - 仅创建 PR:`/git-commit-and-pr --pr --base <BRANCH>`
38
+
39
+ ## 流程
40
+
41
+ ### 一、状态检测(并行)
42
+
43
+ ```bash
44
+ git status --short
45
+ git branch --show-current
46
+ git log -1 --format='%H %s' 2>/dev/null || echo "no-commits"
47
+ ```
48
+
49
+ 补充检测:
50
+
51
+ ```bash
52
+ git rev-parse --abbrev-ref --symbolic-full-name @{u} 2>/dev/null || echo "no-upstream"
53
+ git remote -v
54
+ git rev-parse --abbrev-ref origin/HEAD 2>/dev/null || echo "origin/main"
55
+ git log --oneline -n 5
56
+ git log origin/main..HEAD --oneline
57
+ git diff --stat origin/main...HEAD
58
+ ```
59
+
60
+ 决策规则:
61
+
62
+ - `--issue-only`:仅走 Issue 创建并结束。
63
+ - `--pr`:跳过 Issue/Commit,直接走 PR 流程。
64
+ - 默认模式:
65
+ - 若 `git status --short` 非空:按 `Issue -> Branch -> Commit -> PR` 全链路执行。
66
+ - 若 `git status --short` 为空且 `origin/main..HEAD` 有提交差异:直接执行 PR 创建。
67
+ - 若 `git status --short` 为空且无分支差异:输出“无需创建 PR”,并结束。
68
+
69
+ ### 二、Issue 创建
70
+
71
+ 1. 收集上下文:
72
+ - `git status --short`
73
+ - `git diff --stat`
74
+ - 可选去重:`gh issue list --search "<关键词>" --limit 5`
75
+
76
+ 2. 生成标题:`[模块] 简洁描述` 或 `[类型] 功能/问题描述`。
77
+
78
+ 3. 标签:从 `bug`、`enhancement`、`documentation`、`performance`、`refactor`、`backend`、`frontend`、`infrastructure` 中选择。
79
+
80
+ 4. 正文结构固定:背景、现状/问题、期望行为、执行计划、影响范围、相关资源。
81
+
82
+ 5. 创建命令(heredoc):
83
+
84
+ ```bash
85
+ gh issue create \
86
+ --title "[模块] 问题摘要" \
87
+ --label label1 --label label2 \
88
+ --body-file - <<'MSG'
89
+ ## 背景
90
+ [背景]
91
+
92
+ ## 现状/问题
93
+ [问题]
94
+
95
+ ## 期望行为
96
+ [目标]
97
+
98
+ ## 执行计划
99
+ - [ ] 步骤一
100
+ - [ ] 步骤二
101
+
102
+ ## 影响范围
103
+ [范围]
104
+ MSG
105
+ ```
106
+
107
+ ### 三、分支处理(新增强制步骤)
108
+
109
+ 1. 若当前分支是 `main/master`,必须切新分支后再提交。
110
+ 2. 若存在 Issue 编号,分支名优先:`codex/fix/<issue-id>-<slug>`。
111
+ 3. 若已在合法功能分支且用户显式要求保留,则继续使用当前分支。
112
+
113
+ 示例:
114
+
115
+ ```bash
116
+ git switch -c codex/fix/1234-short-topic
117
+ ```
118
+
119
+ ### 四、Commit 流程
120
+
121
+ 1. 暂存与检查:
122
+
123
+ ```bash
124
+ git add -A
125
+ git diff --cached --stat
126
+ ```
127
+
128
+ 2. 根据 `git diff --cached` 生成提交:
129
+
130
+ ```bash
131
+ git commit -F - <<'EOF'
132
+ <type>: <概要>
133
+
134
+ 变更说明:
135
+ - <变更项一>
136
+ - <变更项二>
137
+
138
+ Refs: #<issue-id>
139
+ EOF
140
+ ```
141
+
142
+ 3. `type` 仅用:`feat/fix/refactor/docs/chore/test`。
143
+
144
+ 4. 提交后确认:
145
+
146
+ ```bash
147
+ git status
148
+ git log -1 --oneline
149
+ ```
150
+
151
+ ### 五、PR 创建
152
+
153
+ 1. 推送分支:
154
+
155
+ ```bash
156
+ git push -u origin HEAD
157
+ ```
158
+
159
+ 2. 变更分析:
160
+
161
+ ```bash
162
+ git log origin/main..HEAD --oneline
163
+ git diff origin/main...HEAD --stat
164
+ ```
165
+
166
+ 3. 创建 PR(默认 `--base main`):
167
+
168
+ ```bash
169
+ gh pr create --base main --title '<type>: <概要>' --body-file - <<'EOF'
170
+ ## 变更说明
171
+
172
+ - <变更项>
173
+
174
+ ## 测试
175
+
176
+ - [ ] 本地测试通过
177
+
178
+ Closes: #<issue-id>
179
+ EOF
180
+ ```
181
+
182
+ 4. 成功后输出评审命令:`/pr-review-loop --pr <PR_NUMBER>`。
183
+
184
+ ## 质量检查
185
+
186
+ - 标题简洁可读,语义明确。
187
+ - 描述包含背景、问题、目标、影响范围。
188
+ - 提交与 PR 文案可核对到 diff。
189
+ - 标签与提交类型匹配。
190
+ - 不泄露敏感信息。
191
+
192
+ ## 失败与阻塞输出
193
+
194
+ 输出以下四项:
195
+
196
+ - 停止阶段
197
+ - 已完成列表
198
+ - 阻塞原因
199
+ - 继续执行命令建议
200
+
201
+ ## 成功输出
202
+
203
+ 输出以下四项:
204
+
205
+ - Issue 编号与标题
206
+ - Commit 摘要
207
+ - PR 编号与链接
208
+ - 下一步评审命令
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "Git Commit 与 PR"
3
+ short_description: "零输入自动执行 Issue、分支、Commit、PR 全流程"
4
+ default_prompt: "使用 $git-commit-and-pr 在当前仓库自动完成 Issue、分支、Commit、PR 全流程。"
@@ -0,0 +1,187 @@
1
+ ---
2
+ name: git-release
3
+ description: 在 Git 仓库中执行标准化版本发布流程并自动生成高质量中文发行说明。用于以下场景:需要从 release 分支发布新版本;需要从分支名提取并校验语义化版本(含 alpha/beta/rc 预发布);需要批量更新多个 package.json 的 version 字段并提交;需要基于最近 GitHub Release 汇总提交与 PR 信息、分类变更、生成发布摘要;需要创建 annotated tag、推送远端并创建 GitHub Release。
4
+ ---
5
+
6
+ # Git Release
7
+
8
+ ## 目标
9
+
10
+ 在 `release/vX.Y.Z` 或 `release/vX.Y.Z-<prerelease>.N` 分支上,完成从发布前检查到 GitHub Release 创建的全流程。
11
+
12
+ ## 执行原则
13
+
14
+ - 全程使用中文输出。
15
+ - 严格执行前置校验,任何硬性条件不满足时立即终止。
16
+ - 发行说明必须结构化、可读、可追溯。
17
+ - 命令默认在仓库根目录执行。
18
+
19
+ ## 流程
20
+
21
+ ### 一、发布前检查
22
+
23
+ 1. 检查工作区是否干净:`git status --porcelain`。
24
+ 2. 若存在未提交变更,列出文件并终止流程。
25
+ 3. 检查当前分支:`git branch --show-current`。
26
+ 4. 仅接受以下正则:`^release/v\d+\.\d+\.\d+(-(alpha|beta|rc)\.\d+)?$`。
27
+ 5. 从分支名提取版本号,例如:
28
+ - `release/v1.2.3` -> `v1.2.3` -> `1.2.3`
29
+ - `release/v1.2.3-beta.2` -> `v1.2.3-beta.2` -> `1.2.3-beta.2`
30
+ 6. 检查目标 tag 是否已存在:`git tag -l "v<VERSION>"`。
31
+ 7. 向用户确认版本号;若用户修改版本号,重新校验格式与 tag 冲突。
32
+
33
+ ### 二、更新版本号
34
+
35
+ 1. 更新以下文件的 `version` 字段为纯版本号(不带 `v` 前缀):
36
+ - `package.json`
37
+ - `apps/backend/package.json`
38
+ - `apps/front/package.json`
39
+ - `apps/admin-front/package.json`
40
+ 2. 仅修改 `version` 字段,不变更其他内容。
41
+ 3. 执行提交:
42
+
43
+ ```bash
44
+ git add package.json apps/*/package.json
45
+ git commit -F - <<'MSG'
46
+ chore: bump version to <VERSION>
47
+
48
+ 更新所有 package.json 版本号为 <VERSION>
49
+
50
+ 发布准备提交
51
+ MSG
52
+ ```
53
+
54
+ ### 三、收集与分析变更
55
+
56
+ 1. 优先获取最近已发布版本:
57
+
58
+ ```bash
59
+ gh release list --limit 1 --json tagName,publishedAt --jq '.[0].tagName'
60
+ ```
61
+
62
+ 2. 若无 GitHub Release,回退:`git describe --tags --abbrev=0`。
63
+ 3. 采集范围:`<last-release-tag>..HEAD`。
64
+ 4. 收集数据:
65
+ - `git log <last-release-tag>..HEAD --oneline`
66
+ - `git log <last-release-tag>..HEAD --pretty=format:"%H|%s|%b"`
67
+ - `git diff <last-release-tag>..HEAD --shortstat`
68
+ 5. 从提交中提取 PR 编号(合并提交、Refs、Closes 等),并用 `gh pr view` 获取标题与标签。
69
+ 6. 去重同一 PR。
70
+ 7. 分类变更:
71
+ - 新增:`feat` 或 feature 标签
72
+ - 优化:`refactor`、`perf`、`chore`
73
+ - 修复:`fix` 或 bug 标签
74
+ - 技术改进:`docs`、`test`、`build`、`ci`
75
+ 8. 过滤噪音:忽略无意义合并记录与 `chore: bump version`。
76
+ 9. 识别运维提醒:环境变量、数据库迁移、依赖更新、配置与部署变更。
77
+
78
+ ### 四、生成发行说明
79
+
80
+ 1. 生成 3-5 条发布摘要,按业务影响排序。
81
+ 2. 输出分类变更清单,关联 PR 或 Issue。
82
+ 3. 使用以下结构:
83
+
84
+ ```markdown
85
+ # v<VERSION> 发行说明
86
+
87
+ ## 发布摘要
88
+
89
+ - <核心变更1> (#PR)
90
+ - <核心变更2> (#PR)
91
+ - <核心变更3> (#PR)
92
+
93
+ 发布日期:<YYYY-MM-DD>
94
+ 对比分支:`<last-tag>...v<VERSION>`
95
+
96
+ ## 新增
97
+
98
+ - <新增项> (#PR)
99
+
100
+ ## 优化
101
+
102
+ - <优化项> (#PR)
103
+
104
+ ## 修复
105
+
106
+ - <修复项> (#PR)
107
+
108
+ ## 技术改进
109
+
110
+ - <技术改进项> (#PR)
111
+
112
+ ## 运维提醒
113
+
114
+ - <提醒项>
115
+
116
+ ## 引用
117
+
118
+ - PRs:#1, #2
119
+ - Issues:#10
120
+ - 共计 <X> 个提交
121
+
122
+ ## 升级指南
123
+
124
+ 1. <步骤1>
125
+ 2. <步骤2>
126
+ ```
127
+
128
+ ### 五、创建发布
129
+
130
+ 1. 创建 annotated tag:
131
+
132
+ ```bash
133
+ git tag -a v<VERSION> -m "Release v<VERSION>"
134
+ ```
135
+
136
+ 2. 推送 tag:
137
+
138
+ ```bash
139
+ git push origin v<VERSION>
140
+ ```
141
+
142
+ 3. 创建 GitHub Release:
143
+
144
+ ```bash
145
+ gh release create v<VERSION> \
146
+ --title "v<VERSION>" \
147
+ --notes-file - <<'EOF'
148
+ <完整发行说明>
149
+ EOF
150
+ ```
151
+
152
+ 4. 输出发布 URL 与发布后检查清单。
153
+
154
+ ## 终止条件
155
+
156
+ 以下任一情况出现时终止流程并给出明确原因:
157
+
158
+ - 工作区存在未提交修改。
159
+ - 当前分支不符合 release 分支命名规则。
160
+ - 版本号格式非法或与现有 tag 冲突。
161
+ - 自上次发布以来无新提交。
162
+
163
+ ## 输出模板
164
+
165
+ ### 发布前状态
166
+
167
+ - 工作目录状态
168
+ - 当前分支
169
+ - 解析出的版本号
170
+ - 版本格式校验结果
171
+ - tag 冲突校验结果
172
+
173
+ ### 变更分析
174
+
175
+ - 基准版本
176
+ - 提交范围
177
+ - 提交数与 PR 数
178
+ - 代码变更统计
179
+ - 分类统计
180
+
181
+ ### 发布结果
182
+
183
+ - 版本号
184
+ - 分支名
185
+ - tag 推送状态
186
+ - Release URL
187
+ - 发布后清单
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "Git Release"
3
+ short_description: "在 release 分支上自动完成版本发布与发行说明生成"
4
+ default_prompt: "使用 $git-release 按规范执行一次发布并生成高质量发行说明。"
@@ -39,6 +39,37 @@ async function collectTemplateFiles(dir) {
39
39
  return out
40
40
  }
41
41
 
42
+ async function collectAllFiles(dir) {
43
+ const out = []
44
+
45
+ async function walk(current) {
46
+ let entries
47
+ try {
48
+ entries = await fs.readdir(current, { withFileTypes: true })
49
+ } catch {
50
+ return
51
+ }
52
+
53
+ for (const entry of entries) {
54
+ const full = join(current, entry.name)
55
+ if (entry.isDirectory()) {
56
+ if (entry.name === '__pycache__' || entry.name === '.pytest_cache') {
57
+ continue
58
+ }
59
+ await walk(full)
60
+ continue
61
+ }
62
+ if (!entry.isFile()) continue
63
+ const lowerName = entry.name.toLowerCase()
64
+ if (lowerName.endsWith('.pyc') || lowerName.endsWith('.pyo') || lowerName.endsWith('.pyd')) continue
65
+ out.push(full)
66
+ }
67
+ }
68
+
69
+ await walk(dir)
70
+ return out
71
+ }
72
+
42
73
  async function ensureDir(path) {
43
74
  await fs.mkdir(path, { recursive: true })
44
75
  }
@@ -84,6 +115,37 @@ async function copyTemplateTree({ srcDir, dstDir }) {
84
115
  return { total: files.length, md: mdCount, py: pyCount, json: jsonCount }
85
116
  }
86
117
 
118
+ async function copyDirMerge({ srcDir, dstDir }) {
119
+ const files = await collectAllFiles(srcDir)
120
+
121
+ for (const file of files) {
122
+ const rel = relative(srcDir, file)
123
+ const target = join(dstDir, rel)
124
+ await ensureDir(dirname(target))
125
+ await fs.copyFile(file, target)
126
+ }
127
+
128
+ return { fileCount: files.length }
129
+ }
130
+
131
+ async function copySkillsDirectories({ srcSkillsDir, dstSkillsDir }) {
132
+ const entries = await fs.readdir(srcSkillsDir, { withFileTypes: true })
133
+ let copiedDirs = 0
134
+ let copiedFiles = 0
135
+
136
+ for (const entry of entries) {
137
+ if (!entry.isDirectory()) continue
138
+ const srcDir = join(srcSkillsDir, entry.name)
139
+ const dstDir = join(dstSkillsDir, entry.name)
140
+ await ensureDir(dstDir)
141
+ const stats = await copyDirMerge({ srcDir, dstDir })
142
+ copiedDirs++
143
+ copiedFiles += stats.fileCount
144
+ }
145
+
146
+ return { copiedDirs, copiedFiles }
147
+ }
148
+
87
149
  function resolveTemplateRoot(packageRoot) {
88
150
  return [join(packageRoot, '@opencode')]
89
151
  }
@@ -117,15 +179,20 @@ export async function runOpenCodeInitial(options = {}) {
117
179
  const srcCommands = join(existingRoot, 'commands')
118
180
  const dstAgents = join(dstRoot, 'agents')
119
181
  const dstCommands = join(dstRoot, 'commands')
182
+ const srcSkills = join(packageRoot, 'codex', 'skills')
183
+ const dstSkills = join(homeDir, '.codex', 'skills')
120
184
 
121
185
  await assertDirExists(srcAgents, '模板目录 agents')
122
186
  await assertDirExists(srcCommands, '模板目录 commands')
187
+ await assertDirExists(srcSkills, '模板目录 codex/skills')
123
188
 
124
189
  await ensureDir(dstAgents)
125
190
  await ensureDir(dstCommands)
191
+ await ensureDir(dstSkills)
126
192
 
127
193
  const agentsStats = await copyTemplateTree({ srcDir: srcAgents, dstDir: dstAgents })
128
194
  const commandsStats = await copyTemplateTree({ srcDir: srcCommands, dstDir: dstCommands })
195
+ const skillsStats = await copySkillsDirectories({ srcSkillsDir: srcSkills, dstSkillsDir: dstSkills })
129
196
 
130
197
  logger.success(`已初始化 OpenCode 模板到: ${dstRoot}`)
131
198
  logger.info(
@@ -138,4 +205,5 @@ export async function runOpenCodeInitial(options = {}) {
138
205
  `${commandsStats.py > 0 ? ` + ${commandsStats.py} 个 .py 文件` : ''}` +
139
206
  `${commandsStats.json > 0 ? ` + ${commandsStats.json} 个 .json 文件` : ''}`
140
207
  )
208
+ logger.info(`skills: ${skillsStats.copiedDirs} 个目录,覆盖复制 ${skillsStats.copiedFiles} 个文件 -> ${dstSkills}`)
141
209
  }
@@ -18,8 +18,9 @@ const TARGET_CONFIGS = {
18
18
  admin: {
19
19
  configFile: 'vercel.admin.json',
20
20
  projectIdEnvVar: 'VERCEL_PROJECT_ID_ADMIN',
21
- deployCwd: 'apps/admin-front',
22
- deployMode: 'prebuilt'
21
+ deployCwd: '.',
22
+ deployMode: 'prebuilt',
23
+ prebuiltCwd: '.'
23
24
  },
24
25
  'telegram-bot': {
25
26
  configFile: 'vercel.telegram-bot.json',
@@ -168,8 +169,13 @@ function readLinkedProjectContext(contextRoot) {
168
169
  }
169
170
 
170
171
  function clearLinkedProjectContext(contextRoot) {
171
- const path = join(contextRoot, VERCEL_PROJECT_LINK_PATH)
172
- rmSync(path, { force: true })
172
+ const linkDir = join(contextRoot, '.vercel')
173
+ rmSync(linkDir, { force: true, recursive: true })
174
+ }
175
+
176
+ function clearVercelBuildOutput(contextRoot) {
177
+ const outputDir = join(contextRoot, '.vercel', 'output')
178
+ rmSync(outputDir, { force: true, recursive: true })
173
179
  }
174
180
 
175
181
  async function runVercel(args, options = {}) {
@@ -375,9 +381,8 @@ export async function deployToVercel(target, options = {}) {
375
381
  `检测到 ${VERCEL_PROJECT_LINK_PATH} 但解析失败: ${linkedContext.parseError.message}`
376
382
  )
377
383
  if (strictContext) {
378
- logger.error('strictContext 已开启,已阻止继续部署以避免回退污染')
379
- process.exitCode = 1
380
- return
384
+ logger.warn('strictContext 已开启:自动清理 .vercel 后继续部署')
385
+ clearLinkedProjectContext(prebuiltCwd)
381
386
  }
382
387
  }
383
388
 
@@ -388,11 +393,11 @@ export async function deployToVercel(target, options = {}) {
388
393
  ` 本地链接: org=${maskIdentifier(linkedContext.orgId)} project=${maskIdentifier(linkedContext.projectId)}`
389
394
  )
390
395
  if (strictContext) {
391
- logger.error('strictContext 已开启,已阻止部署(请清理 .vercel 或修正环境变量)')
392
- process.exitCode = 1
393
- return
396
+ logger.warn('strictContext 已开启:检测到冲突,自动清理 .vercel 后继续部署')
397
+ clearLinkedProjectContext(prebuiltCwd)
398
+ } else {
399
+ logger.warn('strictContext 已关闭,继续执行(可能存在误部署风险)')
394
400
  }
395
- logger.warn('strictContext 已关闭,继续执行(可能存在误部署风险)')
396
401
  }
397
402
 
398
403
  logger.info(
@@ -431,6 +436,8 @@ export async function deployToVercel(target, options = {}) {
431
436
  if (strictContext && process.env.DX_VERCEL_KEEP_LINK !== '1') {
432
437
  clearLinkedProjectContext(prebuiltCwd)
433
438
  }
439
+ // 避免 Vercel 本地预构建产物残留导致 EEXIST(如 _global-error.func 符号链接冲突)
440
+ clearVercelBuildOutput(prebuiltCwd)
434
441
 
435
442
  // 第一步:本地构建
436
443
  logger.step(`本地构建 ${t} (${environment})`)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ranger1/dx",
3
- "version": "0.1.64",
3
+ "version": "0.1.66",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -19,6 +19,7 @@
19
19
  "bin/",
20
20
  "lib/",
21
21
  "@opencode/",
22
+ "codex/",
22
23
  "LICENSE",
23
24
  "README.md",
24
25
  "package.json"