@jspg-ai/coding-bb 0.0.3-beta.8 → 0.0.4
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/cbb/dev-standards/rules/cbb-ai-behavior.md +0 -15
- package/cbb/dev-standards/rules/cbb-workspace-discipline.md +38 -0
- package/cbb/lib/external/index.js +228 -0
- package/cbb/lib/install/agents-nav.js +92 -0
- package/cbb/lib/install/claude-code.js +8 -1
- package/cbb/lib/install/command-skill.js +105 -0
- package/cbb/lib/install/init.js +399 -129
- package/cbb/lib/install/minimax.js +60 -0
- package/cbb/lib/install/rule-rewrite.js +1 -1
- package/cbb/lib/install/setup-decision.js +92 -0
- package/cbb/lib/install/tool-selection.js +23 -0
- package/cbb/lib/install/trae.js +18 -8
- package/cbb/lib/install/workbuddy.js +36 -0
- package/cbb/lib/install/workspaces.js +15 -61
- package/cbb/lib/utils/check-update.js +13 -5
- package/cbb/lib/utils/githook.js +230 -0
- package/cbb/lib/utils/gitignore.js +17 -4
- package/cbb/lib/utils/plugin-hook.js +92 -0
- package/cbb/worktrees/_shared/scripts/check-local-state.js +114 -0
- package/cbb/worktrees/_shared/scripts/check-unarchived.js +77 -0
- package/cbb/worktrees/_shared/scripts/delete-branches.js +147 -0
- package/cbb/worktrees/_shared/scripts/deprecate-branches.js +119 -0
- package/cbb/worktrees/_shared/scripts/find-target-worktree.js +148 -96
- package/cbb/worktrees/_shared/scripts/find-workspace-root.js +8 -8
- package/cbb/worktrees/_shared/scripts/install-git-hook.js +70 -0
- package/cbb/worktrees/_shared/scripts/junction-ai-config.js +192 -0
- package/cbb/worktrees/_shared/scripts/merge-branches.js +469 -0
- package/cbb/worktrees/_shared/scripts/push-core.js +1 -1
- package/cbb/worktrees/_shared/scripts/silence-popup.js +1 -1
- package/cbb/worktrees/_shared/scripts/start-local.js +180 -0
- package/cbb/worktrees/skills/cbb-local-mode/SKILL.md +194 -0
- package/cbb/worktrees/skills/cbb-local-mode/scripts/check-local-state.js +114 -0
- package/cbb/worktrees/skills/cbb-local-mode/scripts/delete-branches.js +147 -0
- package/cbb/worktrees/skills/cbb-local-mode/scripts/deprecate-branches.js +119 -0
- package/cbb/worktrees/skills/cbb-local-mode/scripts/install-git-hook.js +70 -0
- package/cbb/worktrees/skills/cbb-local-mode/scripts/merge-branches.js +469 -0
- package/cbb/worktrees/skills/cbb-local-mode/scripts/silence-popup.js +23 -0
- package/cbb/worktrees/skills/cbb-local-mode/scripts/start-local.js +180 -0
- package/cbb/worktrees/skills/cbb-worktree-close/SKILL.md +133 -81
- package/cbb/worktrees/skills/cbb-worktree-close/scripts/check-env.js +3 -3
- package/cbb/worktrees/skills/cbb-worktree-close/scripts/check-unarchived.js +77 -48
- package/cbb/worktrees/skills/cbb-worktree-close/scripts/delete-branches.js +147 -136
- package/cbb/worktrees/skills/cbb-worktree-close/scripts/deprecate-branches.js +119 -0
- package/cbb/worktrees/skills/cbb-worktree-close/scripts/discover-apps.js +14 -5
- package/cbb/worktrees/skills/cbb-worktree-close/scripts/find-target-worktree.js +148 -96
- package/cbb/worktrees/skills/cbb-worktree-close/scripts/find-workspace-root.js +8 -8
- package/cbb/worktrees/skills/cbb-worktree-close/scripts/junction-ai-config.js +192 -0
- package/cbb/worktrees/skills/cbb-worktree-close/scripts/merge-branches.js +469 -0
- package/cbb/worktrees/skills/cbb-worktree-close/scripts/remove-worktrees.js +44 -9
- package/cbb/worktrees/skills/cbb-worktree-close/scripts/safety-check.js +15 -4
- package/cbb/worktrees/skills/cbb-worktree-close/scripts/silence-popup.js +1 -1
- package/cbb/worktrees/skills/cbb-worktree-init/SKILL.md +136 -123
- package/cbb/worktrees/skills/cbb-worktree-init/scripts/check-env.js +1 -1
- package/cbb/worktrees/skills/cbb-worktree-init/scripts/check-repos.js +2 -2
- package/cbb/worktrees/skills/cbb-worktree-init/scripts/create-branches.js +33 -8
- package/cbb/worktrees/skills/cbb-worktree-init/scripts/create-worktrees.js +60 -46
- package/cbb/worktrees/skills/cbb-worktree-init/scripts/install-git-hook.js +70 -0
- package/cbb/worktrees/skills/cbb-worktree-init/scripts/junction-ai-config.js +192 -0
- package/cbb/worktrees/skills/cbb-worktree-init/scripts/parse-config.js +7 -7
- package/cbb/worktrees/skills/cbb-worktree-init/scripts/push-branches.js +3 -3
- package/cbb/worktrees/skills/cbb-worktree-init/scripts/push-core.js +1 -1
- package/cbb/worktrees/skills/cbb-worktree-init/scripts/silence-popup.js +1 -1
- package/cbb/worktrees/skills/cbb-worktree-init/scripts/sync-repos.js +11 -7
- package/cbb/worktrees/skills/cbb-worktree-init/scripts/update-gitignore.js +37 -9
- package/cbb/worktrees/skills/cbb-worktree-push/SKILL.md +212 -56
- package/cbb/worktrees/skills/cbb-worktree-push/scripts/check-unarchived.js +77 -0
- package/cbb/worktrees/skills/cbb-worktree-push/scripts/commit-worktrees.js +70 -12
- package/cbb/worktrees/skills/cbb-worktree-push/scripts/find-target-worktree.js +148 -96
- package/cbb/worktrees/skills/cbb-worktree-push/scripts/find-workspace-root.js +8 -8
- package/cbb/worktrees/skills/cbb-worktree-push/scripts/make-mr-urls.js +211 -0
- package/cbb/worktrees/skills/cbb-worktree-push/scripts/merge-branches.js +469 -0
- package/cbb/worktrees/skills/cbb-worktree-push/scripts/push-core.js +1 -1
- package/cbb/worktrees/skills/cbb-worktree-push/scripts/push-worktrees.js +67 -20
- package/cbb/worktrees/skills/cbb-worktree-push/scripts/silence-popup.js +1 -1
- package/config/{workspace-config.sample.json → CBB-WORKSPACE.sample.json} +1 -1
- package/config/openspec/schemas/spec-driven/schema.yaml +20 -0
- package/config/upstream-mirrors.json +5 -0
- package/external/frontend-design/.version +6 -0
- package/external/frontend-design/LICENSE.txt +177 -0
- package/external/frontend-design/SKILL.md +71 -0
- package/openspec/.version +2 -2
- package/openspec/commands/archive.md +15 -8
- package/openspec/commands/bulk-archive.md +13 -6
- package/openspec/commands/continue.md +0 -1
- package/openspec/commands/explore.md +1 -1
- package/openspec/commands/ff.md +1 -1
- package/openspec/commands/onboard.md +9 -4
- package/openspec/commands/update.md +11 -5
- package/openspec/commands/verify.md +57 -26
- package/openspec/skills/openspec-apply-change/SKILL.md +1 -1
- package/openspec/skills/openspec-archive-change/SKILL.md +16 -9
- package/openspec/skills/openspec-bulk-archive-change/SKILL.md +14 -7
- package/openspec/skills/openspec-continue-change/SKILL.md +1 -2
- package/openspec/skills/openspec-explore/SKILL.md +2 -2
- package/openspec/skills/openspec-ff-change/SKILL.md +2 -2
- package/openspec/skills/openspec-new-change/SKILL.md +1 -1
- package/openspec/skills/openspec-onboard/SKILL.md +10 -5
- package/openspec/skills/openspec-propose/SKILL.md +1 -1
- package/openspec/skills/openspec-sync-specs/SKILL.md +1 -1
- package/openspec/skills/openspec-update-change/SKILL.md +12 -6
- package/openspec/skills/openspec-verify-change/SKILL.md +59 -27
- package/package.json +3 -2
- package/superpowers/.version +3 -3
- package/superpowers/skills/writing-plans/SKILL.md +32 -20
- package/cbb/lib/install/codebuddy.js +0 -30
- package/cbb/worktrees/skills/cbb-worktree-init/scripts/find-workspace-root.js +0 -67
- package/config/workspace-agents.sample.md +0 -41
- package/superpowers/skills/writing-plans/plan-document-reviewer-prompt.md +0 -49
|
@@ -4,7 +4,7 @@ require('./silence-popup');
|
|
|
4
4
|
* cbb-worktree-init 同步 .codespace/ 仓库
|
|
5
5
|
*
|
|
6
6
|
* 用法:node sync-repos.js <ws_root>
|
|
7
|
-
* - ws_root: 工作空间根绝对路径(apps 从 <ws_root>/
|
|
7
|
+
* - ws_root: 工作空间根绝对路径(apps 从 <ws_root>/CBB-WORKSPACE.json 读取)
|
|
8
8
|
*
|
|
9
9
|
* 对每个 app:
|
|
10
10
|
* - .codespace/<name>/.git 存在 → fetch --all
|
|
@@ -19,6 +19,7 @@ require('./silence-popup');
|
|
|
19
19
|
const { execSync } = require('child_process');
|
|
20
20
|
const fs = require('fs');
|
|
21
21
|
const path = require('path');
|
|
22
|
+
const { installTo: installGitHook } = require('./install-git-hook');
|
|
22
23
|
|
|
23
24
|
const wsRoot = process.argv[2];
|
|
24
25
|
|
|
@@ -28,23 +29,23 @@ if (!wsRoot) {
|
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
/**
|
|
31
|
-
* 从
|
|
32
|
+
* 从 CBB-WORKSPACE.json 读取 apps(避免 shell 字符串传递中的转义问题)
|
|
32
33
|
*/
|
|
33
34
|
function loadAppsFromConfig() {
|
|
34
|
-
const configPath = path.join(wsRoot, '
|
|
35
|
+
const configPath = path.join(wsRoot, 'CBB-WORKSPACE.json');
|
|
35
36
|
if (!fs.existsSync(configPath)) {
|
|
36
|
-
console.error('❌
|
|
37
|
+
console.error('❌ CBB-WORKSPACE.json 不存在:' + configPath);
|
|
37
38
|
return null;
|
|
38
39
|
}
|
|
39
40
|
try {
|
|
40
41
|
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
41
42
|
if (!config.apps || !Array.isArray(config.apps) || config.apps.length === 0) {
|
|
42
|
-
console.error('❌
|
|
43
|
+
console.error('❌ CBB-WORKSPACE.json 中 apps 为空');
|
|
43
44
|
return null;
|
|
44
45
|
}
|
|
45
46
|
return config.apps;
|
|
46
47
|
} catch (e) {
|
|
47
|
-
console.error('❌
|
|
48
|
+
console.error('❌ CBB-WORKSPACE.json 解析失败:' + e.message);
|
|
48
49
|
return null;
|
|
49
50
|
}
|
|
50
51
|
}
|
|
@@ -77,7 +78,10 @@ for (const app of apps) {
|
|
|
77
78
|
// 不存在:clone
|
|
78
79
|
try {
|
|
79
80
|
execSync('git clone -q "' + app.repo + '" "' + target + '"', { stdio: 'pipe' });
|
|
80
|
-
|
|
81
|
+
// 兜底 clone 出的应用同步装分支感知 pre-commit hook(ADR 0002;
|
|
82
|
+
// cbb setup 首次 clone 已装,此处幂等补齐)
|
|
83
|
+
installGitHook(target);
|
|
84
|
+
console.log('✅ ' + app.name + ': 代码已拉取(clone + 提交防线 hook)');
|
|
81
85
|
results.push({ name: app.name, status: 'cloned' });
|
|
82
86
|
} catch (e) {
|
|
83
87
|
console.error('❌ ' + app.name + ': clone 失败 — ' + app.repo);
|
|
@@ -6,13 +6,13 @@ require('./silence-popup');
|
|
|
6
6
|
* 用法:node update-gitignore.js <ws_root> <branch>
|
|
7
7
|
*
|
|
8
8
|
* 行为:
|
|
9
|
-
* 1. stash
|
|
10
|
-
* 2.
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
9
|
+
* 1. stash 当前未提交修改(仅 untracked 时不产生 stash)
|
|
10
|
+
* 2. 目标分支不存在则从 <remote>/<main> 创建(首跑时分支尚未由 create-branches.js 建立,
|
|
11
|
+
* 顺序无关自愈;create-branches.js 后续会报 reused;基线不取 HEAD,见 ADR 0002)
|
|
12
|
+
* 3. 切换到目标分支
|
|
13
|
+
* 4. 检查 .worktrees/ 和 .codespace/ 是否在 .gitignore,缺失则添加并提交
|
|
14
|
+
* 5. 切回原分支
|
|
15
|
+
* 6. pop stash(如有)
|
|
16
16
|
*
|
|
17
17
|
* 幂等:目标分支已在某个 worktree 中检出(init-worktree 重跑场景,上次已处理过
|
|
18
18
|
* .gitignore)时,跳过全部操作直接输出 GITIGNORE_OK——避免 checkout 与 worktree 检出冲突。
|
|
@@ -67,8 +67,12 @@ const status = run('git status --porcelain', wsRoot);
|
|
|
67
67
|
if (status.ok && status.out) {
|
|
68
68
|
const stash = run('git stash push -m "init-worktree-temp"', wsRoot);
|
|
69
69
|
if (stash.ok) {
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
if (/No local changes to save/.test(stash.out)) {
|
|
71
|
+
console.log('ℹ️ 仅 untracked 文件,无需 stash');
|
|
72
|
+
} else {
|
|
73
|
+
stashed = true;
|
|
74
|
+
console.log('✅ 已 stash 未提交修改');
|
|
75
|
+
}
|
|
72
76
|
} else {
|
|
73
77
|
console.error('❌ git stash 失败:' + stash.err);
|
|
74
78
|
process.exit(2);
|
|
@@ -77,6 +81,30 @@ if (status.ok && status.out) {
|
|
|
77
81
|
console.log('ℹ️ 无未提交修改,跳过 stash');
|
|
78
82
|
}
|
|
79
83
|
|
|
84
|
+
// 2.1 目标分支不存在则创建(Step 3.2 先于 Step 3.3 执行时分支尚未建立)
|
|
85
|
+
// 基线取 <remote>/<main>(ADR 0002,与 create-branches.js 一致):本地模式进行中根可能检出在
|
|
86
|
+
// feature 分支,从 HEAD 建会把未合并的开发态带进新分支;无 remote 时回退 HEAD
|
|
87
|
+
const branchList = run('git branch --list "' + branch + '"', wsRoot);
|
|
88
|
+
if (branchList.ok && !branchList.out) {
|
|
89
|
+
const remoteOut = run('git remote', wsRoot);
|
|
90
|
+
let base = 'HEAD';
|
|
91
|
+
if (remoteOut.ok && remoteOut.out) {
|
|
92
|
+
const r = remoteOut.out.split(/\r?\n/)[0];
|
|
93
|
+
const symbolic = run('git symbolic-ref "refs/remotes/' + r + '/HEAD"', wsRoot);
|
|
94
|
+
const main = (symbolic.ok && symbolic.out)
|
|
95
|
+
? symbolic.out.replace('refs/remotes/' + r + '/', '')
|
|
96
|
+
: 'main';
|
|
97
|
+
base = r + '/' + main;
|
|
98
|
+
}
|
|
99
|
+
const create = run('git branch "' + branch + '" "' + base + '"', wsRoot);
|
|
100
|
+
if (!create.ok) {
|
|
101
|
+
console.error('❌ 创建分支 ' + branch + ' 失败:' + create.err);
|
|
102
|
+
if (stashed) run('git stash pop', wsRoot);
|
|
103
|
+
process.exit(2);
|
|
104
|
+
}
|
|
105
|
+
console.log('✅ 分支 ' + branch + ' 不存在,已从 ' + base + ' 创建');
|
|
106
|
+
}
|
|
107
|
+
|
|
80
108
|
// 3. 切换到目标分支
|
|
81
109
|
const checkout = run('git checkout "' + branch + '"', wsRoot);
|
|
82
110
|
if (!checkout.ok) {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: cbb-worktree-push
|
|
3
|
-
description: "提交并推送当前需求的所有关联仓库(跨 worktree
|
|
3
|
+
description: "提交并推送当前需求的所有关联仓库(跨 worktree 批量),推送后提供交付选择:提 MR(预填链接)或由 agent 直接合并到目标分支(主干/测试分支,ADR 0008)。触发:用户说'提交'/'commit'/'push'/'推送'/'提交并push'/'推到远端'/'提MR'/'提合并请求'/'merge request';用户在 .worktrees/worktree-<需求名>/ 下工作要求批量推送;apply 完成后准备推送所有 worktree;close-worktree 前需要先推送未提交改动。注意:本技能会先识别场景——不在 cbb 工作空间时降级为普通单仓库提交推送。"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Push Worktrees(跨平台脚本化版本
|
|
6
|
+
# Push Worktrees(跨平台脚本化版本 v2.0)
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
提交并推送当前需求的所有关联仓库(跨仓库批量,ADR 0002 双布局:worktree 布局或本地模式布局自动发现)。专门解决"开发完成后批量推送"和"用户口语化提交并push"两类场景——本技能是**唯一的远端推送出口**(cbb-worktree-init 自 0.0.3-beta.22 起 lazy push,创建流程零远端副作用;首次 push 自动建立 upstream)。
|
|
9
9
|
|
|
10
10
|
> **本技能描述含宽泛触发词("提交"/"push"),允许被误触发**。因此第一步永远是 **Step 0 场景识别**:在 cbb 工作空间内才走跨 worktree 批量流程;在普通 git 仓库降级为单仓库提交推送;非 git 目录则退出。
|
|
11
11
|
|
|
@@ -15,12 +15,13 @@ description: "提交并推送当前需求的所有关联仓库(跨 worktree
|
|
|
15
15
|
|
|
16
16
|
- 本技能的所有原子操作封装为 `scripts/*.js` Node.js 脚本,**跨平台执行**
|
|
17
17
|
- **两阶段**:先 `commit-worktrees.js` 提交所有 dirty 仓库,再 `push-worktrees.js` 推送所有 worktree 仓库。两个脚本独立退出码,AI agent 区分阶段
|
|
18
|
+
- **push 后交付选择(Step 7,ADR 0008)**:三选一——提 MR(`make-mr-urls.js` 生成预填链接,可自动开浏览器;只生成链接,提交/评审/合并由人在平台完成,不触碰凭据)/ **直接合并**(`merge-branches.js` 把需求分支合入目标分支并推送;需经归档前置、提交检查、目标选择与风险确认)/ 暂不处理
|
|
18
19
|
- 复用 `cbb-worktree-push/scripts/push-core.js` 的 `pushSingle` / `classifyError` —— 与 `cbb-worktree-init` 的 `push-branches.js` 行为完全一致,错误码契约统一
|
|
19
20
|
- `find-workspace-root.js` / `find-target-worktree.js` 为共享脚本:源仓库真源在 `cbb/worktrees/_shared/scripts/`,部署后位于本 skill 的 `scripts/` 下,与 `cbb-worktree-init` / `cbb-worktree-close` 逻辑完全一致
|
|
20
21
|
- **本技能支持两层执行位置**(与 `cbb-worktree-close` 对称):
|
|
21
22
|
- 工作空间根目录(`WS_ROOT/`)
|
|
22
23
|
- worktree 内部(`WS_ROOT/.worktrees/worktree-<需求名>/` 或其子目录)
|
|
23
|
-
-
|
|
24
|
+
- 输入约定:**自由文本**(需求名等)直接在对话中提问并等待回复;**固定选项多选**使用 `AskUserQuestion`。**例外:commit 信息由 agent 自行生成,不提问、不等待确认**
|
|
24
25
|
|
|
25
26
|
---
|
|
26
27
|
|
|
@@ -35,7 +36,7 @@ node "<skill_dir>/scripts/<name>.js" <参数>
|
|
|
35
36
|
```
|
|
36
37
|
|
|
37
38
|
> - `<skill_dir>` = 本 SKILL.md 所在目录的绝对路径(AI 从读取路径即可推出)
|
|
38
|
-
> - **cwd 必须保持在工作空间根目录**:脚本按 cwd 定位 `
|
|
39
|
+
> - **cwd 必须保持在工作空间根目录**:脚本按 cwd 定位 `CBB-WORKSPACE.json` 与 git 仓库
|
|
39
40
|
> - 不要用 `./scripts/...` 裸相对路径(AI 的 cwd 不是技能目录,会解析失败)
|
|
40
41
|
|
|
41
42
|
脚本输出:
|
|
@@ -53,9 +54,9 @@ node "<skill_dir>/scripts/<name>.js" <参数>
|
|
|
53
54
|
|
|
54
55
|
| 场景 | 判据 | 策略 |
|
|
55
56
|
|------|------|------|
|
|
56
|
-
| **A. cbb 工作空间根** | cwd(或其 git 顶层)含 `
|
|
57
|
+
| **A. cbb 工作空间根** | cwd(或其 git 顶层)含 `CBB-WORKSPACE.json`;`find-workspace-root.js` 退出码 0 | 走 **Step 1 → 6** 的跨 worktree 批量流程 |
|
|
57
58
|
| **B. worktree 内** | cwd 命中 `<ws_root>/.worktrees/worktree-<需求名>/` | 用 cwd 反推 `<需求名>`,走 **Step 1 → 6** 批量流程 |
|
|
58
|
-
| **C. 普通 git 仓库** | 在 git 仓库内,但无 `
|
|
59
|
+
| **C. 普通 git 仓库** | 在 git 仓库内,但无 `CBB-WORKSPACE.json`(`find-workspace-root.js` 退出码 1) | **降级为单仓库提交推送**:不调 worktree 脚本,按常规 `git add -u` → agent 生成 commit 信息 → `git commit` → `git push`(信息生成规则与 A/B 场景一致:精炼最简、5–50 字符) |
|
|
59
60
|
| **D. 非 git 目录** | `git rev-parse --is-inside-work-tree` 失败 | 说明"当前目录不是 git 仓库",终止,不做任何操作 |
|
|
60
61
|
|
|
61
62
|
**判场景与定位(A/B 场景):**
|
|
@@ -73,13 +74,13 @@ node "<skill_dir>/scripts/find-workspace-root.js"
|
|
|
73
74
|
**场景 C 降级流程**(普通仓库误触):
|
|
74
75
|
|
|
75
76
|
1. `git status` 报告改动现状
|
|
76
|
-
2.
|
|
77
|
+
2. agent 基于改动内容生成 commit 信息(精炼最简,5–50 字符;规则见 Step 3)
|
|
77
78
|
3. `git add -u`(默认不含未追踪文件;用户明确要求时才 `git add -A`)
|
|
78
|
-
4. `git commit -m "
|
|
79
|
+
4. `git commit -m "<agent 生成的信息>"`
|
|
79
80
|
5. `git push`(无 upstream 时先询问用户是否 `-u`)
|
|
80
81
|
6. 汇报结果;**不要**提及 worktree 批量流程
|
|
81
82
|
|
|
82
|
-
> 场景 C 是兜底:让"提交代码"在普通仓库里也能正常完成,而不是因找不到 `
|
|
83
|
+
> 场景 C 是兜底:让"提交代码"在普通仓库里也能正常完成,而不是因找不到 `CBB-WORKSPACE.json` 报错。
|
|
83
84
|
|
|
84
85
|
---
|
|
85
86
|
|
|
@@ -90,9 +91,9 @@ node "<skill_dir>/scripts/find-target-worktree.js" "<ws_root>"
|
|
|
90
91
|
```
|
|
91
92
|
|
|
92
93
|
**解读末行**:
|
|
93
|
-
- 退出码 0 + 末行(需求名) →
|
|
94
|
-
- 退出码 1 + 末行 `MULTIPLE_WORKTREEES` →
|
|
95
|
-
- 退出码 2 + 末行 `NO_WORKTREE` →
|
|
94
|
+
- 退出码 0 + 末行(需求名) → 唯一识别,自动用之(脚本自动合并 worktree 布局与本地模式布局候选,`(local)` 标本地模式进行中)
|
|
95
|
+
- 退出码 1 + 末行 `MULTIPLE_WORKTREEES` → 多个进行中需求,AI agent 在对话中让用户输入序号或分支名
|
|
96
|
+
- 退出码 2 + 末行 `NO_WORKTREE` → 无进行中需求,提示用户先执行 `cbb-worktree-init`(worktree 模式)或 `cbb-local-mode`(本地模式)
|
|
96
97
|
|
|
97
98
|
---
|
|
98
99
|
|
|
@@ -110,36 +111,27 @@ node "<skill_dir>/scripts/commit-worktrees.js" "<ws_root>" "<branch>" --message
|
|
|
110
111
|
- `skipped (skip-repo)`:用户指定跳过
|
|
111
112
|
- `failed`:错误
|
|
112
113
|
|
|
113
|
-
> ⓘ **dry-run
|
|
114
|
+
> ⓘ **dry-run 必跑**:把将要 commit 的文件清单与 agent 生成的 commit 信息一并展示给用户(透明可见,**不停顿等待回复**),避免误纳 `.idea/` 等被 git 忽略的文件(虽然 `git add -u` 已天然排除,但 dry-run 仍是最后兜底)。
|
|
114
115
|
|
|
115
116
|
---
|
|
116
117
|
|
|
117
|
-
## Step 3
|
|
118
|
+
## Step 3:生成 commit 信息(agent 自主)
|
|
118
119
|
|
|
119
|
-
AI agent
|
|
120
|
+
AI agent 在执行 commit 前**自行生成** commit 信息,**不向用户提问、不等待确认**。
|
|
120
121
|
|
|
121
|
-
|
|
122
|
+
**生成规则(硬约束)**:
|
|
122
123
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
[1] 所有仓库用同一条信息(推荐简单场景)
|
|
131
|
-
[2] 每仓库分别提供(推荐多仓库改不同东西)
|
|
132
|
-
|
|
133
|
-
回复示例(统一):
|
|
134
|
-
feat(<branch>): 实现客户运费逻辑
|
|
135
|
-
|
|
136
|
-
回复示例(分别):
|
|
137
|
-
workspace: chore(<branch>): 同步 openspec 标准
|
|
138
|
-
ep-foo-app: feat(<branch>): 实现 saveClientExpectFreight
|
|
139
|
-
ep-bar-app: refactor(<branch>): 拆分 PlanService
|
|
140
|
-
```
|
|
124
|
+
| 规则 | 要求 |
|
|
125
|
+
|------|------|
|
|
126
|
+
| 长度 | **5–50 字符**(按字符计:1 个中文字 = 1,1 个英文字母 / 空格 / 标点 = 1);超出范围不得提交 |
|
|
127
|
+
| 风格 | 精炼、最简——一句话说清"改了什么",不写缘由、不换行、不堆砌 |
|
|
128
|
+
| 依据 | 以 Step 2 dry-run 的实际改动 + `openspec/changes/<branch>/tasks.md` 已完成 task 为准;禁止凭空编造 |
|
|
129
|
+
| 格式 | 沿用 Conventional Commits 前缀(`feat` / `fix` / `refactor` / `chore` / `docs` 等)+ 需求名,如 `feat(<branch>): 实现客户运费逻辑` |
|
|
130
|
+
| 多仓库 | 各仓库改动语义不同时**逐仓库分别生成**(经 `--per-repo`),禁止硬塞同一条 |
|
|
141
131
|
|
|
142
|
-
|
|
132
|
+
**展示时机**:生成的每条信息在 Step 2 dry-run 预览中随待提交文件一并输出(透明可见),但**不停顿等待回复**,直接进入 Step 4。
|
|
133
|
+
|
|
134
|
+
> ⓘ 用户若在对话中显式给出 commit 信息,直接采用(不覆盖用户意图);未给出时按上述规则自行生成。
|
|
143
135
|
|
|
144
136
|
---
|
|
145
137
|
|
|
@@ -162,7 +154,7 @@ node "<skill_dir>/scripts/commit-worktrees.js" "<ws_root>/.worktrees/worktree-<b
|
|
|
162
154
|
**选项**:
|
|
163
155
|
| 选项 | 说明 |
|
|
164
156
|
|------|------|
|
|
165
|
-
| `--message / -m` | 必填(除非 --per-repo 完全覆盖)。commit
|
|
157
|
+
| `--message / -m` | 必填(除非 --per-repo 完全覆盖)。commit 信息(agent 生成,5–50 字符) |
|
|
166
158
|
| `--per-repo "<name>:msg;<name>:msg"` | 覆盖特定仓库的 commit 信息 |
|
|
167
159
|
| `--untracked` | 同时 stage 未追踪文件(默认 `git add -u`,加此项时 `git add -A`) |
|
|
168
160
|
| `--allow-empty` | 允许空提交 |
|
|
@@ -224,6 +216,153 @@ node "<skill_dir>/scripts/push-worktrees.js" "<ws_root>/.worktrees/worktree-<bra
|
|
|
224
216
|
|
|
225
217
|
---
|
|
226
218
|
|
|
219
|
+
## Step 7:交付选择(MR / 直接合并 / 暂不处理)
|
|
220
|
+
|
|
221
|
+
Step 5 的 `PUSH_RESULT` 中至少一个仓库为 `pushed` / `skipped`(feature 分支已在远端)时,用 `AskUserQuestion` 提案。**动作与目标解耦**(ADR 0008):
|
|
222
|
+
|
|
223
|
+
```
|
|
224
|
+
推送完成。接下来怎么交付?
|
|
225
|
+
[提 MR](推荐) 生成预填的合并请求链接,由人工在浏览器合入
|
|
226
|
+
[直接合并] 由 agent 直接合并到目标分支并推送远端(不经过 MR)
|
|
227
|
+
[暂不处理] 结束本步,分支已推到远端
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### 7.1 目标分支候选(A / B 两出口共用)
|
|
231
|
+
|
|
232
|
+
`git branch -r` 列出**现场实际存在**的候选(主干 / dev / test / release-* 等),交互式让用户指定(**禁止 AI 代决**)。全仓库统一用一个目标分支;远端不存在该分支的仓库自动跳过。
|
|
233
|
+
|
|
234
|
+
### 7.2 出口 A · 提 MR
|
|
235
|
+
|
|
236
|
+
1. 确定目标分支(见 7.1)
|
|
237
|
+
2. 生成链接:
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
node "<skill_dir>/scripts/make-mr-urls.js" "<ws_root>" "<branch>" "<target>" --open
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
**解读末行 `MR_RESULT`**(逐仓库):
|
|
244
|
+
- `ok` → 已生成 URL(`--open` 时已开浏览器;用户在平台页面确认标题/评审人后提交)
|
|
245
|
+
- `no-diff` → 与目标分支无差异,跳过
|
|
246
|
+
- `not-pushed` → feature 分支不在远端(提示先完成 push)
|
|
247
|
+
- `no-target` → 远端无该目标分支(回退重新指定候选)
|
|
248
|
+
- `manual` → origin 无法解析为已知平台,输出手动创建指引;可用 `--platform gitlab|github|gitee` 强制
|
|
249
|
+
|
|
250
|
+
> 平台识别:`github.com` → PR compare、`gitee.com` → compare、**其余默认 GitLab**(企业自建主流);误判用 `--platform` 覆盖。
|
|
251
|
+
|
|
252
|
+
3. **提 MR 后提醒用户后续链路**:测试环境部署 → 用户验收 → 验收通过后 `/opsx:archive` → 正式分支合并走公司发布流程 → 最后 `cbb-worktree-close`(其收尾合并门禁会校验主干合并状态,未合并且非废弃不放行;见 ADR 0007)
|
|
253
|
+
|
|
254
|
+
### 7.3 出口 B · 直接合并(ADR 0008)
|
|
255
|
+
|
|
256
|
+
**前置顺序:先归档(仅目标 = 主干)、后提交检查;两步都满足才可合并。**
|
|
257
|
+
|
|
258
|
+
#### B1 归档前置(仅当目标 = 主干)
|
|
259
|
+
|
|
260
|
+
目标为主干时**合并前必须已归档**;检查(共享脚本):
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
node "<skill_dir>/scripts/check-unarchived.js" "<ws_root>" "<branch>"
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
- 末行 `UNARCHIVED_RESULT` 的 `changes` 为空 → 通过,进 B2
|
|
267
|
+
- 非空 → `AskUserQuestion`:
|
|
268
|
+
|
|
269
|
+
```
|
|
270
|
+
⚠️ 检测到未归档 change:<名称>
|
|
271
|
+
合并进主干前必须已归档(/opsx:archive)。
|
|
272
|
+
|
|
273
|
+
[去归档](推荐) 立即执行归档流程;归档产物落盘后自动回到本步
|
|
274
|
+
[改为提 MR] 转出口 A
|
|
275
|
+
[中止] 结束本步
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
选「去归档」→ agent 执行归档流程(`/opsx:archive`,产物落 `openspec/changes/archive/`)→ 进入 B2(归档产物属未提交改动,由 B2 处理)。
|
|
279
|
+
|
|
280
|
+
> 目标为测试 / 中间分支时**不要求归档**(归档仍在验收通过后执行);「已提交」检查两类目标都适用。
|
|
281
|
+
|
|
282
|
+
#### B2 提交检查(通用)
|
|
283
|
+
|
|
284
|
+
逐仓库 `git status --porcelain`。存在未提交改动(含归档产物)时:
|
|
285
|
+
|
|
286
|
+
```
|
|
287
|
+
⚠️ 检测到未提交改动:
|
|
288
|
+
- workspace: 2 个文件
|
|
289
|
+
|
|
290
|
+
是否提交并推送后再继续合并?
|
|
291
|
+
[提交并推送](推荐) 走提交推送流程,完成后自动回到合并
|
|
292
|
+
[中止] 我先手动处理
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
- 选「提交并推送」→ 执行本技能 Step 2 → Step 5(dry-run 预览 → 批量 commit → 批量 push)→ **直接回到 B3**(不再重复动作选择,避免递归)
|
|
296
|
+
- 选「中止」→ 结束本步(**前置不满足,不予合并**)
|
|
297
|
+
|
|
298
|
+
#### B3 目标分支确认
|
|
299
|
+
|
|
300
|
+
从 7.1 的候选里选定,明确显示主干项:
|
|
301
|
+
|
|
302
|
+
```
|
|
303
|
+
请选择直接合并的目标分支:
|
|
304
|
+
[主干 main] 合并进正式主干(跳过测试与验收)
|
|
305
|
+
[dev] / [test] / … 合并进中间分支(验收后主干仍需另行交付)
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
#### B4 风险提醒 + 确认(必须显式确认)
|
|
309
|
+
|
|
310
|
+
目标 = 主干(强提醒):
|
|
311
|
+
|
|
312
|
+
```
|
|
313
|
+
⚠️ 你选择直接合并到主干 <main> —— 将跳过:
|
|
314
|
+
• 测试环境部署与用户验收
|
|
315
|
+
• 公司正式发布流程
|
|
316
|
+
• MR 评审记录(平台无评审痕迹)
|
|
317
|
+
|
|
318
|
+
合并后内容立即进入主干并推送远端,撤回只能靠 revert 补救。
|
|
319
|
+
|
|
320
|
+
[确认合并] 我已知晓,执行合并
|
|
321
|
+
[改为提 MR] 转出口 A
|
|
322
|
+
[中止] 结束本步
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
目标 = 测试 / 中间分支(弱提醒):
|
|
326
|
+
|
|
327
|
+
```
|
|
328
|
+
⚠️ 你选择直接合并到 <dev> —— 将跳过 MR 评审(平台无记录);
|
|
329
|
+
验收通过后主干仍需另行交付。
|
|
330
|
+
|
|
331
|
+
[确认合并] / [改为提 MR] / [中止]
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
#### B5 执行与报告
|
|
335
|
+
|
|
336
|
+
```bash
|
|
337
|
+
node "<skill_dir>/scripts/merge-branches.js" "<ws_root>" "<branch>" --apps "<app1,app2>" --target "<目标>"
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
- 缺省 / `--target` 等于探测主干 → 主干快路径(本地主干检出上 `merge --no-ff` → push)
|
|
341
|
+
- `--target` 为非主干分支 → **临时 worktree** 合并(基于 `origin/<目标>`,不动用户检出)
|
|
342
|
+
|
|
343
|
+
**解读末行 `MERGE_RESULT`**:
|
|
344
|
+
- 退出码 0 → 交付完成
|
|
345
|
+
- `need-mr`(目标分支受保护)→ 报明细并**转出口 A**
|
|
346
|
+
- `failed`(含 `merge-failed` / `push-failed`)→ 报明细,修复后重跑(幂等)
|
|
347
|
+
- `not-on-main`(仅主干路径)→ 空间根检出被其他需求占用(本地模式进行中):**先收尾该本地需求或改用出口 A 提 MR**;不得用 `--allow-checkout` 抢占(该参数属本地模式收尾,会切走占用者的工作区)
|
|
348
|
+
|
|
349
|
+
报告形态:
|
|
350
|
+
|
|
351
|
+
```
|
|
352
|
+
✅ 直接合并完成
|
|
353
|
+
目标分支:main
|
|
354
|
+
workspace: merged(merge --no-ff + push origin/main)
|
|
355
|
+
jspg-cvat: merged
|
|
356
|
+
jspg-cvat-model-factory: skipped(远端无该分支)
|
|
357
|
+
|
|
358
|
+
后续:主干已交付 → cbb-worktree-close 收尾将直接放行并自动删分支;
|
|
359
|
+
中间分支已交付(dev)→ 验收通过后主干仍需另行交付
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
> **直接合并成功后不删分支**(ADR 0008):分支与 worktree 保留(验收可能需修复),删除由收尾门禁(`cbb-worktree-close`)统一执行。
|
|
363
|
+
|
|
364
|
+
---
|
|
365
|
+
|
|
227
366
|
## 最终报告
|
|
228
367
|
|
|
229
368
|
```
|
|
@@ -238,7 +377,7 @@ node "<skill_dir>/scripts/push-worktrees.js" "<ws_root>/.worktrees/worktree-<bra
|
|
|
238
377
|
workspace: ✅ origin/<branch>
|
|
239
378
|
ep-foo-app: ✅ origin/<branch>
|
|
240
379
|
ep-bar-app: ⚠️ 远程分支已存在(需用户决策 → 已关联)
|
|
241
|
-
|
|
380
|
+
后续:Step 7 交付选择(提 MR 到测试分支 → 部署验收 → /opsx:archive → 正式发布;或直接合并到主干/测试分支,ADR 0008)→ 最后 cbb-worktree-close
|
|
242
381
|
```
|
|
243
382
|
|
|
244
383
|
---
|
|
@@ -249,17 +388,22 @@ node "<skill_dir>/scripts/push-worktrees.js" "<ws_root>/.worktrees/worktree-<bra
|
|
|
249
388
|
|------|----------|
|
|
250
389
|
| **任何触发** | **先做 Step 0 场景识别**,再决定策略 |
|
|
251
390
|
| 场景 A:cwd 在 cbb 工作空间根 | find-workspace-root 成功 → 多个 worktree 时让用户选 |
|
|
252
|
-
| 场景 B:cwd 在 worktree 内 | 自动用 cwd
|
|
253
|
-
| 场景 C:cwd 在普通 git 仓库(无
|
|
391
|
+
| 场景 B:cwd 在 worktree 内 | 自动用 cwd 反推分支,扫描后 agent 生成 commit 信息 |
|
|
392
|
+
| 场景 C:cwd 在普通 git 仓库(无 CBB-WORKSPACE.json) | **降级为单仓库提交推送**,不调用 worktree 脚本 |
|
|
254
393
|
| 场景 D:cwd 非 git 目录 | 说明情况并终止,不做任何操作 |
|
|
255
|
-
| commit
|
|
256
|
-
|
|
|
394
|
+
| commit 信息 | agent 自行生成(5–50 字符,精炼最简),不询问用户;dry-run 预览时一并展示 |
|
|
395
|
+
| 多仓库改动语义不同 | agent 逐仓库分别生成,用 `--per-repo "ep-foo:msg;ep-bar:msg"` |
|
|
257
396
|
| 仓库干净 | commit-worktrees 自动 skipped,只 push 不 commit |
|
|
258
397
|
| 仓库已有 ahead commit | commit-worktrees 自动 skipped(不重复 commit),只 push |
|
|
259
398
|
| 未追踪文件 | 默认不纳入(避免误纳 .idea);用户明确要求时加 `--untracked` |
|
|
260
399
|
| 远程分支已存在 | push 退出码 2,AI 输出元信息后由用户决策(关联/终止) |
|
|
261
400
|
| push 失败(SSH/HTTPS/网络/保护) | push 退出码 2,AI 按错误码分类处理,阻塞并询问用户 |
|
|
262
401
|
| close-worktree 前调用 | 先调本 skill 把 ahead 推送完,避免 close 时丢提交 |
|
|
402
|
+
| push 完成后 | Step 7 交付选择:提 MR → make-mr-urls.js 生成预填链接 + 开浏览器(不代合并);直接合并 → B1 归档(仅主干)→ B2 提交检查 → B3 目标 → B4 风险确认 → merge-branches.js 执行;暂不处理 |
|
|
403
|
+
| 直合并前置 | 目标=主干必须先归档(check-unarchived.js);有未提交改动则询问后走本技能提交推送流程;未确认不合并 |
|
|
404
|
+
| 直合并后 | **不删分支**(ADR 0008),删除归 cbb-worktree-close 收尾门禁 |
|
|
405
|
+
| MR 目标分支 | 交互式:`git branch -r` 列主干候选由用户指定,全仓库统一;缺该分支的仓库自动跳过(no-target) |
|
|
406
|
+
| MR 平台误判 | 非 github.com/gitee.com 默认按 GitLab 拼 URL;有误判用 `--platform gitlab\|github\|gitee` 强制 |
|
|
263
407
|
| --dry-run | 任何时候想预览将要 commit 的内容时使用,不实际修改任何东西 |
|
|
264
408
|
|
|
265
409
|
---
|
|
@@ -271,10 +415,10 @@ node "<skill_dir>/scripts/push-worktrees.js" "<ws_root>/.worktrees/worktree-<bra
|
|
|
271
415
|
- **问题:** 用户在普通仓库说"提交代码",技能被 description 匹配触发,直接跑 `find-workspace-root.js` 失败并报错终止
|
|
272
416
|
- **修复:** Step 0 先判场景;场景 C(普通 git 仓库)降级为单仓库提交推送,不调用 worktree 脚本、不提及批量流程
|
|
273
417
|
|
|
274
|
-
###
|
|
418
|
+
### commit 信息超长或空洞
|
|
275
419
|
|
|
276
|
-
- **问题:** agent
|
|
277
|
-
- **修复:** Step 3
|
|
420
|
+
- **问题:** agent 生成的 commit 信息过长(罗列全部改动、写缘由)或过于笼统("更新代码"),与"5–50 字符、精炼最简"不符
|
|
421
|
+
- **修复:** Step 3 按规则生成——以实际 diff / 已完成 task 为依据,一句话说清改了什么,长度落在 5–50 字符
|
|
278
422
|
|
|
279
423
|
### 误纳未追踪文件
|
|
280
424
|
|
|
@@ -283,18 +427,18 @@ node "<skill_dir>/scripts/push-worktrees.js" "<ws_root>/.worktrees/worktree-<bra
|
|
|
283
427
|
|
|
284
428
|
### close-worktree 前忘记先 push
|
|
285
429
|
|
|
286
|
-
- **问题:** 调 close-worktree 时,
|
|
287
|
-
- **修复:** close-worktree/SKILL.md Step 3.
|
|
430
|
+
- **问题:** 调 close-worktree 时,safety-check 检测到 ahead 提交 → 询问用户「先推送 / 继续 / 中止」;用户没意识到要调本 skill
|
|
431
|
+
- **修复:** close-worktree/SKILL.md Step 3.3 决策点已提示「先推送」选项;本 skill 是该选项的执行入口。走「直合并」出口时,ahead 提交会随合并一并推送到主干
|
|
288
432
|
|
|
289
433
|
### apply 完成后不调 push 直接 close
|
|
290
434
|
|
|
291
|
-
- **问题:** apply 实现完 → 用户关掉 IDE → 调 close-worktree →
|
|
292
|
-
- **修复:**
|
|
435
|
+
- **问题:** apply 实现完 → 用户关掉 IDE → 调 close-worktree → 收尾合并门禁触发(ADR 0007),未合并且非废弃不放行
|
|
436
|
+
- **修复:** 走门禁三选一:提 MR(先经本 skill 推送 feature)或直合并(ahead 提交随合并推送到主干)
|
|
293
437
|
|
|
294
|
-
###
|
|
438
|
+
### 多仓库硬塞同一条 commit 信息
|
|
295
439
|
|
|
296
|
-
- **问题:** workspace 改了
|
|
297
|
-
- **修复:**
|
|
440
|
+
- **问题:** workspace 改了 CBB-WORKSPACE.json,ep-foo-app 改了 Java 代码,硬塞同一条 "feat: 实现 XX" 信息不准确
|
|
441
|
+
- **修复:** agent 逐仓库生成各自的 commit 信息,用 `--per-repo` 传入
|
|
298
442
|
|
|
299
443
|
### push 失败时跳过失败仓库直接继续
|
|
300
444
|
|
|
@@ -312,23 +456,32 @@ node "<skill_dir>/scripts/push-worktrees.js" "<ws_root>/.worktrees/worktree-<bra
|
|
|
312
456
|
|
|
313
457
|
**绝不允许**
|
|
314
458
|
|
|
315
|
-
-
|
|
459
|
+
- commit 信息超出 5–50 字符范围 / 空洞无意义 / 与改动不符
|
|
316
460
|
- 默认 `git add -A` 误纳未追踪文件
|
|
317
461
|
- push 失败后跳过失败仓库直接继续
|
|
318
462
|
- 远程分支已存在时脚本自动 set-upstream-to
|
|
319
463
|
- AI 询问关联选项前不输出 commit 元信息
|
|
320
464
|
- 运行环境 node 不可用时强行继续 → 改为 fail-fast 提示用户
|
|
465
|
+
- MR 出口下代为合并 / 触碰平台凭据 → MR 出口只生成预填链接,提交与合并由人在平台完成。**直合并只能经 Step 7 出口 B 的 `merge-branches.js` 执行(ADR 0008),且必须先过 B1 归档前置、B2 提交检查、B4 风险确认**
|
|
466
|
+
- 直合并跳过风险提醒直接执行 → 目标为主干时必须强提醒并显式确认;**未经确认不得合并**
|
|
467
|
+
- 目标为主干但未归档就合并 → 必须先归档(B1)
|
|
468
|
+
- 直合并成功后擅自删除分支 → 分支保留,删除归收尾门禁(ADR 0007)
|
|
469
|
+
- MR 目标分支由 AI 猜测代决 → 必须列候选让用户指定
|
|
321
470
|
|
|
322
471
|
**必须执行**
|
|
323
472
|
|
|
324
473
|
- 严格按 Step 0 → 1 → 2 → 3 → 4 → 5 → 6 顺序执行(场景 C/D 除外,按 Step 0 降级策略)
|
|
325
474
|
- 所有脚本调用以 `node "<skill_dir>/scripts/<name>.js"` 形式发起(`<skill_dir>` = 本 SKILL.md 所在目录)
|
|
326
475
|
- 执行脚本时 cwd 保持在工作空间根;场景 C 降级时按普通 git 流程
|
|
327
|
-
- Step 2 dry-run
|
|
328
|
-
- Step 3
|
|
329
|
-
- commit
|
|
476
|
+
- Step 2 dry-run 必跑,把待提交文件清单与 agent 生成的 commit 信息一并展示给用户
|
|
477
|
+
- Step 3 由 agent 自行生成 commit 信息(5–50 字符、精炼最简),不向用户提问、不等待确认;生成结果在 dry-run 预览中展示
|
|
478
|
+
- commit 信息必须依据实际 diff / 已完成 task 生成,沿用 Conventional Commits 前缀(feature/refactor/fix 等语义)
|
|
479
|
+
- 多个仓库改动语义不同时必须逐仓库分别生成(`--per-repo`),禁止硬塞同一条
|
|
330
480
|
- commit-worktrees / push-worktrees 退出码 2 时必须阻塞流程
|
|
331
481
|
- 远程分支已存在时 AI 必须先输出 commit 元信息再询问
|
|
482
|
+
- push 成功后必须走 Step 7 交付选择(AskUserQuestion 固定选项;用户选"暂不处理"则正常结束)
|
|
483
|
+
- 直合并严格按 B1(归档,仅主干)→ B2(提交检查)→ B3(目标)→ B4(确认)→ B5(执行)顺序;前置不满足不予合并
|
|
484
|
+
- 直合并目标为中间分支时跳过 B1(不要求归档);两类目标都必须过 B2
|
|
332
485
|
|
|
333
486
|
---
|
|
334
487
|
|
|
@@ -340,4 +493,7 @@ node "<skill_dir>/scripts/push-worktrees.js" "<ws_root>/.worktrees/worktree-<bra
|
|
|
340
493
|
| `find-target-worktree.js` | 目标需求名识别(共享脚本,源仓库真源在 `_shared/scripts/`) | ws_root | 0/1/2 |
|
|
341
494
|
| `commit-worktrees.js` | 批量 commit(默认 `-u`,加 `--untracked` 时 `-A`) | ws_root, branch, --message / --per-repo / --untracked / --allow-empty / --dry-run / --skip-repo | 0/1/2 |
|
|
342
495
|
| `push-worktrees.js` | 批量 push(复用 push-core 错误归类) | ws_root, branch(或 wt_path) | 0/1/2 |
|
|
496
|
+
| `make-mr-urls.js` | 生成预填 MR/PR 创建链接(平台识别 + 远端校验 + diff 判定 + `--open` 开浏览器) | ws_root, branch, target(或 wt_path, target), --platform, --open | 0/1/2 |
|
|
343
497
|
| `push-core.js` | 推送核心(classifyError / pushSingle / collectRemoteMetadata,共享脚本,源仓库真源在 `_shared/scripts/`) | (共享模块) | - |
|
|
498
|
+
| `check-unarchived.js` | 未归档 change 检查(Step 7 直合并的归档前置,共享脚本) | ws_root, branch | 0/1 |
|
|
499
|
+
| `merge-branches.js` | 直合并(Step 7 出口 B / 收尾门禁;预检 → merge --no-ff → push;`--target` 支持测试分支,共享脚本) | ws_root, branch, --apps / --target / --allow-checkout / --dry-run / --check | 0/1/2 |
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
require('./silence-popup');
|
|
3
|
+
/**
|
|
4
|
+
* 未归档 change 检查(共享脚本,真源:cbb/worktrees/_shared/scripts/)
|
|
5
|
+
*
|
|
6
|
+
* 供 cbb-worktree-close(收尾安全评估)与 cbb-worktree-push(Step 7 直合并前置,ADR 0008)调用。
|
|
7
|
+
*
|
|
8
|
+
* 用法:node check-unarchived.js <ws_root> [branch]
|
|
9
|
+
*
|
|
10
|
+
* 双扫(ADR 0002):
|
|
11
|
+
* - <ws_root>/openspec/changes/ → 空间根当前检出(本地模式产物 / 空间根遗留)
|
|
12
|
+
* - <ws_root>/.worktrees/worktree-<branch>/openspec/changes/ → worktree 模式产物
|
|
13
|
+
* (branch 未传时跳过 worktree 扫描,保持旧调用兼容)
|
|
14
|
+
*
|
|
15
|
+
* 输出:changes 为两处合集(去重);detail 标注每个 change 的来源
|
|
16
|
+
* ('root' / 'worktree',同名两处共存时为 'both')。
|
|
17
|
+
* 本检查为信息收集性质,不阻断流程。
|
|
18
|
+
*
|
|
19
|
+
* 退出码:
|
|
20
|
+
* 0 - 检查完成(无论是否有未归档 change)
|
|
21
|
+
* 1 - 参数错误
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
const fs = require('fs');
|
|
25
|
+
const path = require('path');
|
|
26
|
+
|
|
27
|
+
const wsRoot = process.argv[2];
|
|
28
|
+
const branch = process.argv[3];
|
|
29
|
+
|
|
30
|
+
if (!wsRoot) {
|
|
31
|
+
console.error('Usage: node check-unarchived.js <ws_root> [branch]');
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 扫描单个 changes 目录,返回未归档 change 名数组(目录不存在返回 [])。
|
|
37
|
+
*/
|
|
38
|
+
function scanDir(changesDir) {
|
|
39
|
+
if (!fs.existsSync(changesDir)) return [];
|
|
40
|
+
let entries;
|
|
41
|
+
try {
|
|
42
|
+
entries = fs.readdirSync(changesDir, { withFileTypes: true });
|
|
43
|
+
} catch (e) {
|
|
44
|
+
console.error('Failed to read ' + changesDir + ':', e.message);
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
return entries
|
|
48
|
+
.filter(e => e.isDirectory() && e.name !== 'archive')
|
|
49
|
+
.map(e => e.name);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const rootChanges = scanDir(path.join(wsRoot, 'openspec', 'changes'));
|
|
53
|
+
|
|
54
|
+
const worktreeDir = branch
|
|
55
|
+
? path.join(wsRoot, '.worktrees', 'worktree-' + branch, 'openspec', 'changes')
|
|
56
|
+
: null;
|
|
57
|
+
const wtChanges = worktreeDir ? scanDir(worktreeDir) : [];
|
|
58
|
+
|
|
59
|
+
const detail = {};
|
|
60
|
+
for (const name of rootChanges) detail[name] = 'root';
|
|
61
|
+
for (const name of wtChanges) {
|
|
62
|
+
detail[name] = detail[name] === 'root' ? 'both' : 'worktree';
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const changes = Object.keys(detail).sort();
|
|
66
|
+
|
|
67
|
+
if (rootChanges.length === 0 && wtChanges.length === 0) {
|
|
68
|
+
console.log('ℹ️ 未发现未归档 change(根与 worktree' + (worktreeDir ? '' : '(未指定 branch,跳过)') + '均无)');
|
|
69
|
+
} else {
|
|
70
|
+
console.log('ℹ️ 未归档 change:' + (changes.join(', ') || '无'));
|
|
71
|
+
for (const name of changes) {
|
|
72
|
+
console.log(' - ' + name + '(来源:' + detail[name] + ')');
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
console.log('UNARCHIVED_RESULT', JSON.stringify({ changes, detail }));
|
|
77
|
+
process.exit(0);
|