@jspg-ai/coding-bb 0.0.3-beta.23 → 0.0.3-beta.25
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 +3 -3
- package/cbb/lib/install/init.js +23 -21
- package/cbb/lib/install/workspaces.js +15 -15
- package/cbb/lib/utils/githook.js +9 -7
- package/cbb/worktrees/_shared/scripts/find-workspace-root.js +7 -7
- package/cbb/worktrees/_shared/scripts/install-git-hook.js +1 -1
- package/cbb/worktrees/_shared/scripts/start-local.js +4 -4
- package/cbb/worktrees/skills/cbb-local-mode/SKILL.md +1 -1
- package/cbb/worktrees/skills/cbb-local-mode/scripts/install-git-hook.js +1 -1
- package/cbb/worktrees/skills/cbb-local-mode/scripts/start-local.js +4 -4
- package/cbb/worktrees/skills/cbb-worktree-close/SKILL.md +11 -11
- package/cbb/worktrees/skills/cbb-worktree-close/scripts/check-env.js +3 -3
- package/cbb/worktrees/skills/cbb-worktree-close/scripts/delete-branches.js +1 -1
- package/cbb/worktrees/skills/cbb-worktree-close/scripts/discover-apps.js +14 -5
- package/cbb/worktrees/skills/cbb-worktree-close/scripts/find-workspace-root.js +7 -7
- package/cbb/worktrees/skills/cbb-worktree-close/scripts/remove-worktrees.js +11 -2
- package/cbb/worktrees/skills/cbb-worktree-close/scripts/safety-check.js +11 -2
- package/cbb/worktrees/skills/cbb-worktree-init/SKILL.md +25 -25
- 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 +3 -3
- package/cbb/worktrees/skills/cbb-worktree-init/scripts/create-worktrees.js +23 -7
- package/cbb/worktrees/skills/cbb-worktree-init/scripts/find-workspace-root.js +7 -7
- package/cbb/worktrees/skills/cbb-worktree-init/scripts/install-git-hook.js +1 -1
- 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/sync-repos.js +6 -6
- package/cbb/worktrees/skills/cbb-worktree-push/SKILL.md +7 -7
- package/cbb/worktrees/skills/cbb-worktree-push/scripts/commit-worktrees.js +30 -8
- package/cbb/worktrees/skills/cbb-worktree-push/scripts/find-workspace-root.js +7 -7
- package/cbb/worktrees/skills/cbb-worktree-push/scripts/push-worktrees.js +30 -8
- package/config/{workspace-config.sample.json → CBB-WORKSPACE.sample.json} +1 -1
- package/config/workspace-agents.sample.md +4 -44
- package/package.json +1 -1
|
@@ -5,8 +5,8 @@ require('./silence-popup');
|
|
|
5
5
|
* 唯一真源:cbb/worktrees/_shared/scripts/find-workspace-root.js
|
|
6
6
|
* 由 scripts/sync-shared.js 展开到各 skill 的 scripts/ 下(init / extend / close / push)。
|
|
7
7
|
*
|
|
8
|
-
* 优先:git worktree list 第一行(主仓库根目录,不受 worktree 内
|
|
9
|
-
* 兜底:当前目录含
|
|
8
|
+
* 优先:git worktree list 第一行(主仓库根目录,不受 worktree 内 CBB-WORKSPACE.json 副本干扰)
|
|
9
|
+
* 兜底:当前目录含 CBB-WORKSPACE.json
|
|
10
10
|
*
|
|
11
11
|
* 退出码:
|
|
12
12
|
* 0 - 定位成功,输出 WS_ROOT 绝对路径
|
|
@@ -44,19 +44,19 @@ if (worktreeList.ok) {
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
// 兜底:当前目录含
|
|
47
|
+
// 兜底:当前目录含 CBB-WORKSPACE.json
|
|
48
48
|
if (!wsRoot) {
|
|
49
49
|
const cwd = process.cwd();
|
|
50
|
-
if (fs.existsSync(path.join(cwd, '
|
|
50
|
+
if (fs.existsSync(path.join(cwd, 'CBB-WORKSPACE.json'))) {
|
|
51
51
|
wsRoot = cwd;
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
// 校验
|
|
56
|
-
if (!wsRoot || !fs.existsSync(path.join(wsRoot, '
|
|
56
|
+
if (!wsRoot || !fs.existsSync(path.join(wsRoot, 'CBB-WORKSPACE.json'))) {
|
|
57
57
|
console.error('❌ 未找到工作空间根目录');
|
|
58
|
-
console.error(' 原因:git worktree list 失败且当前目录无
|
|
59
|
-
console.error(' 请在业务工作空间根目录(由 cbb setup 初始化、包含
|
|
58
|
+
console.error(' 原因:git worktree list 失败且当前目录无 CBB-WORKSPACE.json');
|
|
59
|
+
console.error(' 请在业务工作空间根目录(由 cbb setup 初始化、包含 CBB-WORKSPACE.json)下执行本技能');
|
|
60
60
|
process.exit(1);
|
|
61
61
|
}
|
|
62
62
|
|
|
@@ -83,14 +83,23 @@ function removeWt(repoPath, wtPath, name) {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
// 1. 应用 worktrees
|
|
86
|
-
const configPath = path.join(wsRoot, '
|
|
86
|
+
const configPath = path.join(wsRoot, 'CBB-WORKSPACE.json');
|
|
87
87
|
let config = {};
|
|
88
88
|
try { config = JSON.parse(fs.readFileSync(configPath, 'utf-8')); } catch (e) {}
|
|
89
89
|
|
|
90
90
|
const apps = config.apps || [];
|
|
91
|
+
/** 解析 app worktree 物理路径:新布局 .codespace/<app> 优先,兼容 ≤beta.23 旧平铺 <app> */
|
|
92
|
+
function resolveAppWt(appName) {
|
|
93
|
+
const nested = path.join(wtDir, '.codespace', appName);
|
|
94
|
+
if (fs.existsSync(path.join(nested, '.git'))) return nested;
|
|
95
|
+
const legacy = path.join(wtDir, appName);
|
|
96
|
+
if (fs.existsSync(path.join(legacy, '.git'))) return legacy;
|
|
97
|
+
return nested;
|
|
98
|
+
}
|
|
99
|
+
|
|
91
100
|
console.log('=== 移除应用 Worktrees ===');
|
|
92
101
|
for (const app of apps) {
|
|
93
|
-
const appWt =
|
|
102
|
+
const appWt = resolveAppWt(app.name);
|
|
94
103
|
const appPath = path.join(wsRoot, '.codespace', app.name);
|
|
95
104
|
removeWt(appPath, appWt, app.name);
|
|
96
105
|
}
|
|
@@ -152,19 +152,28 @@ function checkRepo(name, repoPath, wtPath) {
|
|
|
152
152
|
return { name, wt: wtPath, fetch_ok: fetchOk, dirty_files: dirtyFiles, merged, main, remote_exists: remoteExists, remote: remoteName, unpushed_count: unpushedCount };
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
-
const configPath = path.join(wsRoot, '
|
|
155
|
+
const configPath = path.join(wsRoot, 'CBB-WORKSPACE.json');
|
|
156
156
|
let config = {};
|
|
157
157
|
try { config = JSON.parse(fs.readFileSync(configPath, 'utf-8')); } catch (e) {}
|
|
158
158
|
|
|
159
159
|
const apps = config.apps || [];
|
|
160
160
|
const wtDir = path.join(wsRoot, '.worktrees', 'worktree-' + branch);
|
|
161
161
|
|
|
162
|
+
/** 解析 app worktree 物理路径:新布局 .codespace/<app> 优先,兼容 ≤beta.23 旧平铺 <app> */
|
|
163
|
+
function resolveAppWt(appName) {
|
|
164
|
+
const nested = path.join(wtDir, '.codespace', appName);
|
|
165
|
+
if (fs.existsSync(path.join(nested, '.git'))) return nested;
|
|
166
|
+
const legacy = path.join(wtDir, appName);
|
|
167
|
+
if (fs.existsSync(path.join(legacy, '.git'))) return legacy;
|
|
168
|
+
return nested;
|
|
169
|
+
}
|
|
170
|
+
|
|
162
171
|
const results = [];
|
|
163
172
|
|
|
164
173
|
results.push(checkRepo('workspace', wsRoot, wtDir));
|
|
165
174
|
|
|
166
175
|
for (const app of apps) {
|
|
167
|
-
const appWt =
|
|
176
|
+
const appWt = resolveAppWt(app.name);
|
|
168
177
|
const appPath = path.join(wsRoot, '.codespace', app.name);
|
|
169
178
|
results.push(checkRepo(app.name, appPath, appWt));
|
|
170
179
|
}
|
|
@@ -12,13 +12,13 @@ description: "为一个需求在 workspace 与各关联应用仓库同步创建
|
|
|
12
12
|
## 核心原则
|
|
13
13
|
|
|
14
14
|
- 本技能的所有原子操作封装为 `scripts/*.js` Node.js 脚本,**跨平台执行**(Windows PowerShell / macOS bash / Linux bash / Git Bash 均无需适配)
|
|
15
|
-
-
|
|
15
|
+
- 物理位置(ADR 0003):workspace worktree 在 `.worktrees/worktree-<需求名>/`,各 app worktree 收拢在其 `.codespace/<应用名>/` 容器内——**镜像空间根布局**(应用检出恒在 `.codespace/<app>/`,相对路径两模式通用)
|
|
16
16
|
- 所有仓库使用统一的分支名(不加前缀);worktree 目录名自动加 `worktree-` 前缀;**分支基线统一取 `origin/<main>`(ADR 0002,不取 HEAD)**
|
|
17
17
|
- **lazy push(ADR 0002)**:本技能**不向远端推送任何分支**——远端副作用收敛到 `cbb-worktree-push` 唯一出口;推送失败早暴露的职责由 push 技能的 dry-run 承担
|
|
18
18
|
- **AI 配置 junction(ADR 0002 防线 B)**:创建 worktree 后把根的 AI 配置**内容子目录**(rules / skills / commands)junction 进工作树——开发会话开在 `<wt_path>`(上游 `/opsx:*` 相对路径原样生效,零翻译);工具状态落在工作树自建空壳目录,不互相污染;`cbb update` 在根跑一次全 worktree 生效
|
|
19
|
-
- **本技能必须在业务工作空间根目录执行**(即由 `cbb setup` 初始化、包含 `
|
|
19
|
+
- **本技能必须在业务工作空间根目录执行**(即由 `cbb setup` 初始化、包含 `CBB-WORKSPACE.json` 的目录)
|
|
20
20
|
- 输入约定:**自由文本**(需求名等)直接在对话中提问并等待回复,禁止使用 `AskUserQuestion`;`AskUserQuestion` 仅用于固定选项决策场景(如 push 失败处理、远程分支已存在时的二选一)
|
|
21
|
-
- 关联应用默认为 `
|
|
21
|
+
- 关联应用默认为 `CBB-WORKSPACE.json` 的**全量 apps**;支持按需求语境传入**应用子集**(见 Step 1),需要增减应用时编辑该文件并重跑本技能(幂等)
|
|
22
22
|
- **模式选择(ADR 0002)**:接到需求先判断用户要哪种模式——要**并行 / 隔离** → 本技能;**单线程快改、当前会话就地干** → 引导 `cbb-local-mode`。用户明确指定即遵从,agent 只给推荐不代决
|
|
23
23
|
|
|
24
24
|
---
|
|
@@ -34,7 +34,7 @@ node "<skill_dir>/scripts/<name>.js" <参数>
|
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
> - `<skill_dir>` = 本 SKILL.md 所在目录的绝对路径(AI 从读取路径即可推出)
|
|
37
|
-
> - **cwd 必须保持在工作空间根目录**:脚本按 cwd 定位 `
|
|
37
|
+
> - **cwd 必须保持在工作空间根目录**:脚本按 cwd 定位 `CBB-WORKSPACE.json` 与 git 仓库
|
|
38
38
|
> - 不要用 `./scripts/...` 裸相对路径(AI 的 cwd 不是技能目录,会解析失败)
|
|
39
39
|
|
|
40
40
|
脚本输出:
|
|
@@ -47,7 +47,7 @@ node "<skill_dir>/scripts/<name>.js" <参数>
|
|
|
47
47
|
- `<branch>`:需求名字符串
|
|
48
48
|
- `<wt_path>`:worktree 绝对路径
|
|
49
49
|
|
|
50
|
-
所有脚本读
|
|
50
|
+
所有脚本读 CBB-WORKSPACE.json:app 列表从 `<ws_root>/CBB-WORKSPACE.json` 读取,不需要传 JSON 参数。
|
|
51
51
|
|
|
52
52
|
---
|
|
53
53
|
|
|
@@ -80,7 +80,7 @@ node "<skill_dir>/scripts/check-env-deep.js"
|
|
|
80
80
|
- Node.js >= 16.7.0
|
|
81
81
|
- `.worktrees/` 目录可写
|
|
82
82
|
|
|
83
|
-
### 0.2 读取并校验
|
|
83
|
+
### 0.2 读取并校验 CBB-WORKSPACE.json
|
|
84
84
|
|
|
85
85
|
```bash
|
|
86
86
|
node "<skill_dir>/scripts/parse-config.js"
|
|
@@ -93,23 +93,23 @@ node "<skill_dir>/scripts/parse-config.js"
|
|
|
93
93
|
|
|
94
94
|
### 0.3 配置引导
|
|
95
95
|
|
|
96
|
-
当 `
|
|
96
|
+
当 `CBB-WORKSPACE.json` 缺失、`apps` 为空或格式错误时,向用户说明情况并给出两个选项:
|
|
97
97
|
|
|
98
98
|
```
|
|
99
|
-
⚠️ 当前工作空间尚未配置关联应用(
|
|
99
|
+
⚠️ 当前工作空间尚未配置关联应用(CBB-WORKSPACE.json 缺失或 apps 为空)。
|
|
100
100
|
worktree 需要知道要关联哪些应用仓库。
|
|
101
101
|
|
|
102
102
|
请选择:
|
|
103
|
-
[1] 我自行配置
|
|
104
|
-
[2] 提供应用仓库地址,我帮你写入
|
|
103
|
+
[1] 我自行配置 CBB-WORKSPACE.json,配好后告诉你继续
|
|
104
|
+
[2] 提供应用仓库地址,我帮你写入 CBB-WORKSPACE.json
|
|
105
105
|
```
|
|
106
106
|
|
|
107
107
|
**选项 1:用户自行配置**
|
|
108
108
|
- 停止当前技能流程
|
|
109
|
-
- 提示用户:*"请编辑
|
|
109
|
+
- 提示用户:*"请编辑 CBB-WORKSPACE.json 后回复'继续',或重新执行 init-worktree。"*
|
|
110
110
|
- 用户回复"继续"后,重新从 Step 0 开始校验
|
|
111
111
|
|
|
112
|
-
**选项 2:帮用户写入
|
|
112
|
+
**选项 2:帮用户写入 CBB-WORKSPACE.json**
|
|
113
113
|
- 直接在对话中请用户提供应用信息,格式示例:
|
|
114
114
|
```
|
|
115
115
|
应用名: ep-user-auth-app
|
|
@@ -120,7 +120,7 @@ worktree 需要知道要关联哪些应用仓库。
|
|
|
120
120
|
仓库地址: git@code.example.com:team/ep-user-info-server.git
|
|
121
121
|
类型: back(可选值:front 前端 / back 后端 / mixed 前后端混合)
|
|
122
122
|
```
|
|
123
|
-
- 解析后写入 `
|
|
123
|
+
- 解析后写入 `CBB-WORKSPACE.json`:
|
|
124
124
|
```json
|
|
125
125
|
{
|
|
126
126
|
"apps": [
|
|
@@ -131,20 +131,20 @@ worktree 需要知道要关联哪些应用仓库。
|
|
|
131
131
|
```
|
|
132
132
|
- 写入成功后提交到当前分支(确保 Step 2 状态检查通过):
|
|
133
133
|
```bash
|
|
134
|
-
git add
|
|
135
|
-
git commit -m "chore: add
|
|
134
|
+
git add CBB-WORKSPACE.json
|
|
135
|
+
git commit -m "chore: add CBB-WORKSPACE.json"
|
|
136
136
|
```
|
|
137
137
|
> 若当前不在主分支上,提示用户切回主分支后再提交;若已在主分支上则直接提交。
|
|
138
138
|
- 提交后重新从 Step 0 校验,通过后继续 Step 1
|
|
139
139
|
|
|
140
|
-
> ⚠️ **禁止**:在
|
|
140
|
+
> ⚠️ **禁止**:在 CBB-WORKSPACE.json 无效时跳过本步骤直接创建 worktree,否则无法确定关联应用。
|
|
141
141
|
|
|
142
142
|
---
|
|
143
143
|
|
|
144
144
|
## Step 1:关联应用与代码同步
|
|
145
145
|
|
|
146
146
|
> ⓘ **关联应用 = 需求涉及的应用子集**:从需求语境提取("改订单导出" → 订单相关应用);纯 workspace 内容的需求 → 仅 workspace。子集判断错了零代价——重跑本技能幂等补齐(已存在的分支/worktree 自动复用,详见「幂等重跑」章节)。涉及全部应用 / 不确定时用全量。
|
|
147
|
-
> 需要增减应用时,编辑 `
|
|
147
|
+
> 需要增减应用时,编辑 `CBB-WORKSPACE.json` 后**重跑本技能**即可。
|
|
148
148
|
|
|
149
149
|
### 1.1 同步 `.codespace/` 目录代码
|
|
150
150
|
|
|
@@ -154,7 +154,7 @@ node "<skill_dir>/scripts/sync-repos.js" "<ws_root>"
|
|
|
154
154
|
```
|
|
155
155
|
|
|
156
156
|
**用法**:
|
|
157
|
-
- `<ws_root>`:工作空间根绝对路径(apps 由脚本内部从 `<ws_root>/
|
|
157
|
+
- `<ws_root>`:工作空间根绝对路径(apps 由脚本内部从 `<ws_root>/CBB-WORKSPACE.json` 读取,全量 fetch/clone)
|
|
158
158
|
|
|
159
159
|
**解读输出**:
|
|
160
160
|
- 退出码 0 + 末行 `SYNC_RESULT [...]` → 全部成功
|
|
@@ -280,7 +280,7 @@ node "<skill_dir>/scripts/create-worktrees.js" "<ws_root>" "<branch>" [--apps "a
|
|
|
280
280
|
脚本内部自动完成:
|
|
281
281
|
1. **元数据自愈**:仅 `git worktree prune`(清理"目录已缺失/失效"的注册项);**绝不删除任何物理目录**(0.0.3-beta.11 起——旧版"清理无元数据残留目录"因 Windows 路径分隔符不匹配会误删活跃 worktree,已整体移除)
|
|
282
282
|
2. **workspace worktree 创建**:`git worktree add .worktrees/worktree-<branch> <branch>`
|
|
283
|
-
3. **app worktree
|
|
283
|
+
3. **app worktree 创建**(收拢在 workspace worktree 的 `.codespace/` 容器内,镜像空间根布局);≤beta.23 旧平铺布局自动 `git worktree move` 迁移
|
|
284
284
|
|
|
285
285
|
**解读输出**:
|
|
286
286
|
- 退出码 0 + 末行 `WT_PATH <绝对路径>` → 全部成功,AI 解析出 worktree 绝对路径供后续 Step 4 使用
|
|
@@ -333,7 +333,7 @@ node "<skill_dir>/scripts/junction-ai-config.js" "<ws_root>" "<wt_path>"
|
|
|
333
333
|
|
|
334
334
|
```
|
|
335
335
|
✅ worktree 已创建:<wt_path>
|
|
336
|
-
关联应用:<应用名 →
|
|
336
|
+
关联应用:<应用名 → .codespace/<应用名> 路径> 列表(应用子集或全量)
|
|
337
337
|
🔗 AI 配置已 junction 就位:rules / skills / commands(cbb update 一处生效)
|
|
338
338
|
📌 开发会话请开在 <wt_path>(新开 IDE 窗口 / agent 会话);本根会话保留作控制面
|
|
339
339
|
👉 下一步建议:
|
|
@@ -354,7 +354,7 @@ node "<skill_dir>/scripts/junction-ai-config.js" "<ws_root>" "<wt_path>"
|
|
|
354
354
|
|
|
355
355
|
| 场景 | 重跑行为 |
|
|
356
356
|
|------|----------|
|
|
357
|
-
| 空间新增了应用(编辑 `
|
|
357
|
+
| 空间新增了应用(编辑 `CBB-WORKSPACE.json` 后) | Step 1 拉取新应用代码 → Step 3.3 为新应用建分支 → Step 3.5 在 `.codespace/` 容器内创建缺失的 app worktree → Step 3.6 补 junction |
|
|
358
358
|
| 上次 init 中途失败 | 已存在的分支 / worktree 自动复用,`.gitignore` 处理自动跳过,仅补齐缺失部分 |
|
|
359
359
|
| 应用从 config 移除 | 已创建的分支 / worktree **不会**自动删除;如需清理用 `cbb-worktree-close` skill 关闭后重建 |
|
|
360
360
|
| **本地模式升级 worktree**(ADR 0002) | Step 3.3 分支已存在 → reused(本地阶段提交随身带走);先 `git checkout main` 腾出各仓库本地检出,再重跑本技能 |
|
|
@@ -385,10 +385,10 @@ node "<skill_dir>/scripts/junction-ai-config.js" "<ws_root>" "<wt_path>"
|
|
|
385
385
|
|
|
386
386
|
## 常见错误
|
|
387
387
|
|
|
388
|
-
### 跳过
|
|
388
|
+
### 跳过 CBB-WORKSPACE.json 直接硬编码仓库地址
|
|
389
389
|
|
|
390
390
|
- **问题:** 直接在代码中写死仓库 URL 或本地路径,不同团队的仓库地址不同
|
|
391
|
-
- **修复:** 始终从 `
|
|
391
|
+
- **修复:** 始终从 `CBB-WORKSPACE.json` 读取应用与远程仓库的映射;配置缺失/为空时按 Step 0.3 配置引导处理,不支持在流程中临时指定配置外的仓库
|
|
392
392
|
|
|
393
393
|
### 状态检查对 app 仓库执行完整五项检查
|
|
394
394
|
|
|
@@ -468,7 +468,7 @@ node "<skill_dir>/scripts/junction-ai-config.js" "<ws_root>" "<wt_path>"
|
|
|
468
468
|
- 所有仓库分支统一从 `origin/<main>` 创建(`git branch <需求名> ${REMOTE}/${MAIN}`),不依赖本地检出状态(本地模式进行中重跑不受污染)
|
|
469
469
|
- 严格 Step 3.3 → Step 3.5 顺序(先统一创建分支,再统一创建 worktree;workspace 工作树先建,各应用按序补建)
|
|
470
470
|
- Step 3.6 junction AI 配置(失败不阻塞,但要报告)
|
|
471
|
-
- 所有脚本调用以 `node "<skill_dir>/scripts/<name>.js" "<绝对路径>" "<branch>"` 形式发起(apps 缺省由脚本内部从
|
|
471
|
+
- 所有脚本调用以 `node "<skill_dir>/scripts/<name>.js" "<绝对路径>" "<branch>"` 形式发起(apps 缺省由脚本内部从 CBB-WORKSPACE.json 读取,子集用 `--apps` 传入);**执行时 cwd 保持在工作空间根**(`check-env.js` / `parse-config.js` 按 cwd 定位)
|
|
472
472
|
- Step 4 交接必须声明"开发会话开在 `<wt_path>`、根会话保留作控制面"
|
|
473
473
|
|
|
474
474
|
---
|
|
@@ -478,7 +478,7 @@ node "<skill_dir>/scripts/junction-ai-config.js" "<ws_root>" "<wt_path>"
|
|
|
478
478
|
| 脚本 | 功能 | 参数 | 退出码 |
|
|
479
479
|
|------|------|------|--------|
|
|
480
480
|
| `check-env.js` | 环境探活 | 无 | 0/1/2 |
|
|
481
|
-
| `parse-config.js` | 解析
|
|
481
|
+
| `parse-config.js` | 解析 CBB-WORKSPACE.json | 无 | 0/1/2 |
|
|
482
482
|
| `sync-repos.js` | 同步 .codespace/ 仓库(兜底 clone 时补装提交防线 hook) | ws_root | 0/1/2 |
|
|
483
483
|
| `check-repos.js` | 状态检查 | ws_root | 0/1/2 |
|
|
484
484
|
| `update-gitignore.js` | 安全更新 .gitignore | ws_root, branch | 0/1/2 |
|
|
@@ -48,7 +48,7 @@ console.log('✅ git OK:', checks.git.out);
|
|
|
48
48
|
const insideRepo = tryExec('git rev-parse --is-inside-work-tree');
|
|
49
49
|
if (!insideRepo.ok || insideRepo.out !== 'true') {
|
|
50
50
|
console.error('❌ 当前目录不是 Git 仓库');
|
|
51
|
-
console.error(' init-worktree 必须在工作空间根目录(包含
|
|
51
|
+
console.error(' init-worktree 必须在工作空间根目录(包含 CBB-WORKSPACE.json)下执行');
|
|
52
52
|
process.exit(2);
|
|
53
53
|
}
|
|
54
54
|
console.log('✅ 当前目录是 git 仓库');
|
|
@@ -36,7 +36,7 @@ if (!wsRoot) {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
function loadAppsFromConfig() {
|
|
39
|
-
const configPath = path.join(wsRoot, "
|
|
39
|
+
const configPath = path.join(wsRoot, "CBB-WORKSPACE.json");
|
|
40
40
|
if (!fs.existsSync(configPath)) return null;
|
|
41
41
|
try {
|
|
42
42
|
const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
@@ -46,7 +46,7 @@ function loadAppsFromConfig() {
|
|
|
46
46
|
|
|
47
47
|
const apps = loadAppsFromConfig();
|
|
48
48
|
if (!apps) {
|
|
49
|
-
console.error("
|
|
49
|
+
console.error("CBB-WORKSPACE.json missing or apps empty");
|
|
50
50
|
process.exit(1);
|
|
51
51
|
}
|
|
52
52
|
|
|
@@ -25,7 +25,7 @@ if (appsFlagIdx >= 0) {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
function loadAppsFromConfig() {
|
|
28
|
-
const configPath = path.join(wsRoot, '
|
|
28
|
+
const configPath = path.join(wsRoot, 'CBB-WORKSPACE.json');
|
|
29
29
|
if (!fs.existsSync(configPath)) return null;
|
|
30
30
|
try {
|
|
31
31
|
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
@@ -35,7 +35,7 @@ function loadAppsFromConfig() {
|
|
|
35
35
|
|
|
36
36
|
const allApps = loadAppsFromConfig();
|
|
37
37
|
if (!allApps) {
|
|
38
|
-
console.error('
|
|
38
|
+
console.error('CBB-WORKSPACE.json missing or apps empty');
|
|
39
39
|
process.exit(1);
|
|
40
40
|
}
|
|
41
41
|
|
|
@@ -46,7 +46,7 @@ const apps = appFilter
|
|
|
46
46
|
if (appFilter) {
|
|
47
47
|
const unknown = [...appFilter].filter(n => !allApps.some(a => a.name === n));
|
|
48
48
|
if (unknown.length > 0) {
|
|
49
|
-
console.error('❌ --apps 中的应用不在
|
|
49
|
+
console.error('❌ --apps 中的应用不在 CBB-WORKSPACE.json:' + unknown.join(', '));
|
|
50
50
|
process.exit(1);
|
|
51
51
|
}
|
|
52
52
|
console.log('ℹ️ 应用子集(' + apps.length + '/' + allApps.length + '):' + apps.map(a => a.name).join(', '));
|
|
@@ -4,7 +4,7 @@ require('./silence-popup');
|
|
|
4
4
|
* cbb-worktree-init 创建 Worktree(自愈清理仅限元数据 prune)
|
|
5
5
|
*
|
|
6
6
|
* 用法:node create-worktrees.js <ws_root> <branch>
|
|
7
|
-
* (apps 由脚本内部从 <ws_root>/
|
|
7
|
+
* (apps 由脚本内部从 <ws_root>/CBB-WORKSPACE.json 读取)
|
|
8
8
|
*
|
|
9
9
|
* 行为:
|
|
10
10
|
* 1. 自愈清理:仅 git worktree prune(元数据),**绝不删除任何物理目录**
|
|
@@ -41,7 +41,7 @@ if (appsFlagIdx >= 0) {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
function loadAppsFromConfig() {
|
|
44
|
-
const configPath = path.join(wsRoot, '
|
|
44
|
+
const configPath = path.join(wsRoot, 'CBB-WORKSPACE.json');
|
|
45
45
|
if (!fs.existsSync(configPath)) return null;
|
|
46
46
|
try {
|
|
47
47
|
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
@@ -51,7 +51,7 @@ function loadAppsFromConfig() {
|
|
|
51
51
|
|
|
52
52
|
const allApps = loadAppsFromConfig();
|
|
53
53
|
if (!allApps) {
|
|
54
|
-
console.error('❌
|
|
54
|
+
console.error('❌ CBB-WORKSPACE.json 缺失或 apps 为空');
|
|
55
55
|
process.exit(1);
|
|
56
56
|
}
|
|
57
57
|
|
|
@@ -62,7 +62,7 @@ const apps = appFilter
|
|
|
62
62
|
if (appFilter) {
|
|
63
63
|
const unknown = [...appFilter].filter(n => !allApps.some(a => a.name === n));
|
|
64
64
|
if (unknown.length > 0) {
|
|
65
|
-
console.error('❌ --apps 中的应用不在
|
|
65
|
+
console.error('❌ --apps 中的应用不在 CBB-WORKSPACE.json:' + unknown.join(', '));
|
|
66
66
|
process.exit(1);
|
|
67
67
|
}
|
|
68
68
|
console.log('ℹ️ 应用子集(' + apps.length + '/' + allApps.length + '):' + apps.map(a => a.name).join(', '));
|
|
@@ -109,12 +109,13 @@ if (fs.existsSync(wtDir)) {
|
|
|
109
109
|
console.log('✅ workspace worktree 已创建:' + wtDir);
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
// 3. apps worktree
|
|
112
|
+
// 3. apps worktree 创建(物理嵌套于 workspace worktree 的 .codespace/ 容器内,镜像主布局,ADR 0003)
|
|
113
113
|
console.log('=== 创建 App Worktrees ===');
|
|
114
114
|
const results = [];
|
|
115
115
|
for (const app of apps) {
|
|
116
116
|
const appPath = path.join(wsRoot, '.codespace', app.name);
|
|
117
|
-
const nestedWt = path.join(wtDir, app.name);
|
|
117
|
+
const nestedWt = path.join(wtDir, '.codespace', app.name);
|
|
118
|
+
const legacyWt = path.join(wtDir, app.name);
|
|
118
119
|
|
|
119
120
|
if (fs.existsSync(nestedWt) && fs.existsSync(path.join(nestedWt, '.git'))) {
|
|
120
121
|
console.log('⏭️ ' + app.name + ': 嵌套 worktree 已存在,复用');
|
|
@@ -122,8 +123,23 @@ for (const app of apps) {
|
|
|
122
123
|
continue;
|
|
123
124
|
}
|
|
124
125
|
|
|
125
|
-
//
|
|
126
|
+
// 防御性兜底:容器目录不存在则创建
|
|
126
127
|
fs.mkdirSync(path.dirname(nestedWt), { recursive: true });
|
|
128
|
+
|
|
129
|
+
// 存量迁移:≤beta.23 旧平铺布局 → .codespace/ 容器(worktree 平移,分支与提交随身带走)
|
|
130
|
+
if (fs.existsSync(legacyWt) && fs.existsSync(path.join(legacyWt, '.git'))) {
|
|
131
|
+
const moved = run('git worktree move "' + legacyWt + '" "' + nestedWt + '"', appPath);
|
|
132
|
+
if (moved.ok) {
|
|
133
|
+
console.log('🔁 ' + app.name + ': 旧平铺 worktree 已迁移到 .codespace/ 容器');
|
|
134
|
+
results.push({ name: app.name, status: 'migrated' });
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
console.error('❌ ' + app.name + ': 旧平铺 worktree 迁移失败(常见原因:目录被 IDE 等进程占用):' + moved.err);
|
|
138
|
+
console.error(' 请关闭占用 ' + legacyWt + ' 的程序后重跑本技能;或先执行 cbb-worktree-close 再重建');
|
|
139
|
+
results.push({ name: app.name, status: 'failed', error: moved.err });
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
|
|
127
143
|
const created = run('git worktree add "' + nestedWt + '" "' + branch + '"', appPath);
|
|
128
144
|
if (created.ok) {
|
|
129
145
|
console.log('✅ ' + app.name + ': 嵌套 worktree 创建成功');
|
|
@@ -5,8 +5,8 @@ require('./silence-popup');
|
|
|
5
5
|
* 唯一真源:cbb/worktrees/_shared/scripts/find-workspace-root.js
|
|
6
6
|
* 由 scripts/sync-shared.js 展开到各 skill 的 scripts/ 下(init / extend / close / push)。
|
|
7
7
|
*
|
|
8
|
-
* 优先:git worktree list 第一行(主仓库根目录,不受 worktree 内
|
|
9
|
-
* 兜底:当前目录含
|
|
8
|
+
* 优先:git worktree list 第一行(主仓库根目录,不受 worktree 内 CBB-WORKSPACE.json 副本干扰)
|
|
9
|
+
* 兜底:当前目录含 CBB-WORKSPACE.json
|
|
10
10
|
*
|
|
11
11
|
* 退出码:
|
|
12
12
|
* 0 - 定位成功,输出 WS_ROOT 绝对路径
|
|
@@ -44,19 +44,19 @@ if (worktreeList.ok) {
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
// 兜底:当前目录含
|
|
47
|
+
// 兜底:当前目录含 CBB-WORKSPACE.json
|
|
48
48
|
if (!wsRoot) {
|
|
49
49
|
const cwd = process.cwd();
|
|
50
|
-
if (fs.existsSync(path.join(cwd, '
|
|
50
|
+
if (fs.existsSync(path.join(cwd, 'CBB-WORKSPACE.json'))) {
|
|
51
51
|
wsRoot = cwd;
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
// 校验
|
|
56
|
-
if (!wsRoot || !fs.existsSync(path.join(wsRoot, '
|
|
56
|
+
if (!wsRoot || !fs.existsSync(path.join(wsRoot, 'CBB-WORKSPACE.json'))) {
|
|
57
57
|
console.error('❌ 未找到工作空间根目录');
|
|
58
|
-
console.error(' 原因:git worktree list 失败且当前目录无
|
|
59
|
-
console.error(' 请在业务工作空间根目录(由 cbb setup 初始化、包含
|
|
58
|
+
console.error(' 原因:git worktree list 失败且当前目录无 CBB-WORKSPACE.json');
|
|
59
|
+
console.error(' 请在业务工作空间根目录(由 cbb setup 初始化、包含 CBB-WORKSPACE.json)下执行本技能');
|
|
60
60
|
process.exit(1);
|
|
61
61
|
}
|
|
62
62
|
|
|
@@ -20,7 +20,7 @@ const HOOK_FILE = 'pre-commit';
|
|
|
20
20
|
const BACKUP_SUFFIX = '.cbb-backup';
|
|
21
21
|
|
|
22
22
|
// 与 cbb/lib/utils/githook.js 的 buildPreCommitSource() 输出完全一致(构建时注入)
|
|
23
|
-
const PRE_COMMIT_SOURCE = "#!/usr/bin/env node\n// managed-by-cbb v1 —— 分支感知提交防线(ADR 0002)\n// main 上:workspace 仓库只放行控制面白名单,应用仓库全拦;非 main 一律放行。\n// 绕过逃生口仅限用户显式 git commit --no-verify。\nconst { execSync } = require('child_process');\nconst fs = require('fs');\nfunction run(cmd) {\n try { return execSync(cmd, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }).trim(); }\n catch (e) { return null; }\n}\nconst branch = run('git branch --show-current') || '';\n// 主干名:优先远端 HEAD 指向;探测失败按本地 main/master 存在性回退\nlet main = null;\nconst remotes = run('git remote');\nif (remotes) {\n const r = remotes.split(/\\r?\\n/)[0];\n const sym = run('git symbolic-ref \"refs/remotes/' + r + '/HEAD\"');\n if (sym) main = sym.replace('refs/remotes/' + r + '/', '');\n}\nif (!main) {\n if (run('git rev-parse --verify --quiet refs/heads/main')) main = 'main';\n else if (run('git rev-parse --verify --quiet refs/heads/master')) main = 'master';\n}\n// 非主干(含 detached / 探测失败)→ 放行\nif (!main || branch !== main) process.exit(0);\n\n// 主干上的提交:收集 staged 变更(空提交放行)\nconst staged = run('git diff --cached --name-only') || '';\nconst files = staged.split(/\\r?\\n/).filter(Boolean);\nif (files.length === 0) process.exit(0);\n\n// 仓库类型:workspace 仓库 = 检出含
|
|
23
|
+
const PRE_COMMIT_SOURCE = "#!/usr/bin/env node\n// managed-by-cbb v1 —— 分支感知提交防线(ADR 0002)\n// main 上:workspace 仓库只放行控制面白名单,应用仓库全拦;非 main 一律放行。\n// 绕过逃生口仅限用户显式 git commit --no-verify。\nconst { execSync } = require('child_process');\nconst fs = require('fs');\nfunction run(cmd) {\n try { return execSync(cmd, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }).trim(); }\n catch (e) { return null; }\n}\nconst branch = run('git branch --show-current') || '';\n// 主干名:优先远端 HEAD 指向;探测失败按本地 main/master 存在性回退\nlet main = null;\nconst remotes = run('git remote');\nif (remotes) {\n const r = remotes.split(/\\r?\\n/)[0];\n const sym = run('git symbolic-ref \"refs/remotes/' + r + '/HEAD\"');\n if (sym) main = sym.replace('refs/remotes/' + r + '/', '');\n}\nif (!main) {\n if (run('git rev-parse --verify --quiet refs/heads/main')) main = 'main';\n else if (run('git rev-parse --verify --quiet refs/heads/master')) main = 'master';\n}\n// 非主干(含 detached / 探测失败)→ 放行\nif (!main || branch !== main) process.exit(0);\n\n// 主干上的提交:收集 staged 变更(空提交放行)\nconst staged = run('git diff --cached --name-only') || '';\nconst files = staged.split(/\\r?\\n/).filter(Boolean);\nif (files.length === 0) process.exit(0);\n\n// 仓库类型:workspace 仓库 = 检出含 CBB-WORKSPACE.json(或旧名,改名过渡期识别)\nconst isWorkspace = fs.existsSync('CBB-WORKSPACE.json') || fs.existsSync('workspace-config.json');\n\nfunction deny(bad) {\n console.error('[cbb-guard] 主干上拦截载荷提交(ADR 0002):');\n bad.forEach(f => console.error(' ' + f));\n console.error('需求载荷须在需求分支上提交:本地模式先 git checkout -b <需求名>;worktree 模式在 .worktrees/ 工作树内提交。');\n console.error('控制面白名单:CBB-WORKSPACE.json / .gitignore / AGENTS.md / openspec/config.yaml / openspec/schemas/** / .cbb/**');\n console.error('如确需在主干提交,须由用户显式 git commit --no-verify。');\n process.exit(1);\n}\n\nif (isWorkspace) {\n // 旧名仅放行删除条目:更名迁移提交(git mv)会产生旧名删除 + 新名新增两条\n const EXACT = new Set(['CBB-WORKSPACE.json', 'workspace-config.json', '.gitignore', 'AGENTS.md', 'openspec/config.yaml']);\n const PREFIXES = ['openspec/schemas/', '.cbb/'];\n const bad = files.filter(f => !EXACT.has(f) && !PREFIXES.some(p => f.indexOf(p) === 0));\n if (bad.length) deny(bad);\n} else {\n deny(files);\n}\nprocess.exit(0);\n";
|
|
24
24
|
|
|
25
25
|
function isCbbHookContent(content) {
|
|
26
26
|
return typeof content === 'string' && content.includes(HOOK_MARKER);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
require('./silence-popup');
|
|
3
3
|
/**
|
|
4
|
-
* cbb-worktree-init 读取并校验
|
|
4
|
+
* cbb-worktree-init 读取并校验 CBB-WORKSPACE.json
|
|
5
5
|
*
|
|
6
6
|
* 退出码:
|
|
7
7
|
* 0 - 成功,输出应用列表 JSON
|
|
@@ -15,10 +15,10 @@ require('./silence-popup');
|
|
|
15
15
|
const fs = require('fs');
|
|
16
16
|
const path = require('path');
|
|
17
17
|
|
|
18
|
-
const configPath = path.join(process.cwd(), '
|
|
18
|
+
const configPath = path.join(process.cwd(), 'CBB-WORKSPACE.json');
|
|
19
19
|
|
|
20
20
|
if (!fs.existsSync(configPath)) {
|
|
21
|
-
console.error('❌
|
|
21
|
+
console.error('❌ CBB-WORKSPACE.json 不存在');
|
|
22
22
|
console.error(' 请先在 cbb setup 阶段创建,或手动配置');
|
|
23
23
|
console.error('---');
|
|
24
24
|
console.error('CONFIG_MISSING');
|
|
@@ -29,7 +29,7 @@ let config;
|
|
|
29
29
|
try {
|
|
30
30
|
config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
31
31
|
} catch (e) {
|
|
32
|
-
console.error('❌
|
|
32
|
+
console.error('❌ CBB-WORKSPACE.json 解析失败:', e.message);
|
|
33
33
|
console.error(' 请检查 JSON 格式');
|
|
34
34
|
console.error('---');
|
|
35
35
|
console.error('CONFIG_INVALID');
|
|
@@ -38,8 +38,8 @@ try {
|
|
|
38
38
|
|
|
39
39
|
const apps = config.apps || [];
|
|
40
40
|
if (apps.length === 0) {
|
|
41
|
-
console.error('❌
|
|
42
|
-
console.error(' 请编辑
|
|
41
|
+
console.error('❌ CBB-WORKSPACE.json 中 apps 为空');
|
|
42
|
+
console.error(' 请编辑 CBB-WORKSPACE.json 配置关联应用');
|
|
43
43
|
console.error('---');
|
|
44
44
|
console.error('CONFIG_EMPTY');
|
|
45
45
|
process.exit(2);
|
|
@@ -61,7 +61,7 @@ for (const app of apps) {
|
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
console.log('✅
|
|
64
|
+
console.log('✅ CBB-WORKSPACE.json 有效(' + apps.length + ' 个应用)');
|
|
65
65
|
console.log('---');
|
|
66
66
|
console.log('CONFIG_OK', JSON.stringify(apps));
|
|
67
67
|
process.exit(0);
|
|
@@ -20,7 +20,7 @@ const fs = require('fs');
|
|
|
20
20
|
const path = require('path');
|
|
21
21
|
|
|
22
22
|
function loadAppsFromConfig(wsRoot) {
|
|
23
|
-
const configPath = path.join(wsRoot, '
|
|
23
|
+
const configPath = path.join(wsRoot, 'CBB-WORKSPACE.json');
|
|
24
24
|
if (!fs.existsSync(configPath)) return null;
|
|
25
25
|
try {
|
|
26
26
|
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
@@ -40,7 +40,7 @@ function executePush(opts) {
|
|
|
40
40
|
const branch = opts.branch;
|
|
41
41
|
const apps = opts.apps != null ? opts.apps : loadAppsFromConfig(wsRoot);
|
|
42
42
|
if (apps == null) {
|
|
43
|
-
throw new Error('
|
|
43
|
+
throw new Error('CBB-WORKSPACE.json missing or apps empty');
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
const results = [];
|
|
@@ -68,7 +68,7 @@ function main() {
|
|
|
68
68
|
|
|
69
69
|
const apps = loadAppsFromConfig(wsRoot);
|
|
70
70
|
if (apps == null) {
|
|
71
|
-
console.error('❌
|
|
71
|
+
console.error('❌ CBB-WORKSPACE.json 缺失或 apps 为空');
|
|
72
72
|
process.exit(1);
|
|
73
73
|
}
|
|
74
74
|
|
|
@@ -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
|
|
@@ -29,23 +29,23 @@ if (!wsRoot) {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
|
-
* 从
|
|
32
|
+
* 从 CBB-WORKSPACE.json 读取 apps(避免 shell 字符串传递中的转义问题)
|
|
33
33
|
*/
|
|
34
34
|
function loadAppsFromConfig() {
|
|
35
|
-
const configPath = path.join(wsRoot, '
|
|
35
|
+
const configPath = path.join(wsRoot, 'CBB-WORKSPACE.json');
|
|
36
36
|
if (!fs.existsSync(configPath)) {
|
|
37
|
-
console.error('❌
|
|
37
|
+
console.error('❌ CBB-WORKSPACE.json 不存在:' + configPath);
|
|
38
38
|
return null;
|
|
39
39
|
}
|
|
40
40
|
try {
|
|
41
41
|
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
42
42
|
if (!config.apps || !Array.isArray(config.apps) || config.apps.length === 0) {
|
|
43
|
-
console.error('❌
|
|
43
|
+
console.error('❌ CBB-WORKSPACE.json 中 apps 为空');
|
|
44
44
|
return null;
|
|
45
45
|
}
|
|
46
46
|
return config.apps;
|
|
47
47
|
} catch (e) {
|
|
48
|
-
console.error('❌
|
|
48
|
+
console.error('❌ CBB-WORKSPACE.json 解析失败:' + e.message);
|
|
49
49
|
return null;
|
|
50
50
|
}
|
|
51
51
|
}
|
|
@@ -36,7 +36,7 @@ node "<skill_dir>/scripts/<name>.js" <参数>
|
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
> - `<skill_dir>` = 本 SKILL.md 所在目录的绝对路径(AI 从读取路径即可推出)
|
|
39
|
-
> - **cwd 必须保持在工作空间根目录**:脚本按 cwd 定位 `
|
|
39
|
+
> - **cwd 必须保持在工作空间根目录**:脚本按 cwd 定位 `CBB-WORKSPACE.json` 与 git 仓库
|
|
40
40
|
> - 不要用 `./scripts/...` 裸相对路径(AI 的 cwd 不是技能目录,会解析失败)
|
|
41
41
|
|
|
42
42
|
脚本输出:
|
|
@@ -54,9 +54,9 @@ node "<skill_dir>/scripts/<name>.js" <参数>
|
|
|
54
54
|
|
|
55
55
|
| 场景 | 判据 | 策略 |
|
|
56
56
|
|------|------|------|
|
|
57
|
-
| **A. cbb 工作空间根** | cwd(或其 git 顶层)含 `
|
|
57
|
+
| **A. cbb 工作空间根** | cwd(或其 git 顶层)含 `CBB-WORKSPACE.json`;`find-workspace-root.js` 退出码 0 | 走 **Step 1 → 6** 的跨 worktree 批量流程 |
|
|
58
58
|
| **B. worktree 内** | cwd 命中 `<ws_root>/.worktrees/worktree-<需求名>/` | 用 cwd 反推 `<需求名>`,走 **Step 1 → 6** 批量流程 |
|
|
59
|
-
| **C. 普通 git 仓库** | 在 git 仓库内,但无 `
|
|
59
|
+
| **C. 普通 git 仓库** | 在 git 仓库内,但无 `CBB-WORKSPACE.json`(`find-workspace-root.js` 退出码 1) | **降级为单仓库提交推送**:不调 worktree 脚本,按常规 `git add -u` → 向用户询问 commit 信息 → `git commit` → `git push`(保留本 skill 的"必问 commit 信息"约束) |
|
|
60
60
|
| **D. 非 git 目录** | `git rev-parse --is-inside-work-tree` 失败 | 说明"当前目录不是 git 仓库",终止,不做任何操作 |
|
|
61
61
|
|
|
62
62
|
**判场景与定位(A/B 场景):**
|
|
@@ -80,7 +80,7 @@ node "<skill_dir>/scripts/find-workspace-root.js"
|
|
|
80
80
|
5. `git push`(无 upstream 时先询问用户是否 `-u`)
|
|
81
81
|
6. 汇报结果;**不要**提及 worktree 批量流程
|
|
82
82
|
|
|
83
|
-
> 场景 C 是兜底:让"提交代码"在普通仓库里也能正常完成,而不是因找不到 `
|
|
83
|
+
> 场景 C 是兜底:让"提交代码"在普通仓库里也能正常完成,而不是因找不到 `CBB-WORKSPACE.json` 报错。
|
|
84
84
|
|
|
85
85
|
---
|
|
86
86
|
|
|
@@ -123,7 +123,7 @@ AI agent 在执行真实 commit 前**必须**在对话中向用户确认 commit
|
|
|
123
123
|
|
|
124
124
|
```
|
|
125
125
|
检测到以下 worktree 仓库有未提交改动:
|
|
126
|
-
- workspace: 5 个文件(
|
|
126
|
+
- workspace: 5 个文件(CBB-WORKSPACE.json, openspec/changes/foo/...)
|
|
127
127
|
- ep-foo-app: 12 个文件
|
|
128
128
|
- ep-bar-app: 3 个文件
|
|
129
129
|
|
|
@@ -284,7 +284,7 @@ node "<skill_dir>/scripts/make-mr-urls.js" "<ws_root>" "<branch>" "<target>" --o
|
|
|
284
284
|
| **任何触发** | **先做 Step 0 场景识别**,再决定策略 |
|
|
285
285
|
| 场景 A:cwd 在 cbb 工作空间根 | find-workspace-root 成功 → 多个 worktree 时让用户选 |
|
|
286
286
|
| 场景 B:cwd 在 worktree 内 | 自动用 cwd 反推分支,扫描后进入询问 commit 信息 |
|
|
287
|
-
| 场景 C:cwd 在普通 git 仓库(无
|
|
287
|
+
| 场景 C:cwd 在普通 git 仓库(无 CBB-WORKSPACE.json) | **降级为单仓库提交推送**,不调用 worktree 脚本 |
|
|
288
288
|
| 场景 D:cwd 非 git 目录 | 说明情况并终止,不做任何操作 |
|
|
289
289
|
| commit 信息缺失 | 必须询问用户;禁止自动生成(可基于 apply task 上下文给建议) |
|
|
290
290
|
| 多仓库需不同 commit 信息 | 用 `--per-repo "ep-foo:m1;ep-bar:m2"`,必须由用户逐条提供 |
|
|
@@ -330,7 +330,7 @@ node "<skill_dir>/scripts/make-mr-urls.js" "<ws_root>" "<branch>" "<target>" --o
|
|
|
330
330
|
|
|
331
331
|
### 多仓库用同一条 commit 信息
|
|
332
332
|
|
|
333
|
-
- **问题:** workspace 改了
|
|
333
|
+
- **问题:** workspace 改了 CBB-WORKSPACE.json,ep-foo-app 改了 Java 代码,硬塞同一条 "feat: 实现 XX" 信息不准确
|
|
334
334
|
- **修复:** 用 `--per-repo` 模式,让用户逐仓库提供
|
|
335
335
|
|
|
336
336
|
### push 失败时跳过失败仓库直接继续
|