@jspg-ai/coding-bb 0.0.5-beta.4 → 0.0.5-beta.6

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.
@@ -34,15 +34,8 @@ trigger: always_on
34
34
 
35
35
  **只改必须改的,只清理自己造成的烂摊子。**
36
36
 
37
- 编辑已有代码时:
38
- - 不顺手"改进"相邻代码、注释或格式
39
- - 不重构没坏的东西
40
- - 匹配现有风格,即使你更习惯另一种写法
41
- - 发现无关的死代码,口头提及即可,不要删除
42
-
43
- 当你的改动产生孤儿代码时:
44
- - 删除因你的改动而不再使用的 import / 变量 / 函数
45
- - 不删除已有的死代码,除非用户要求
37
+ - 编辑已有代码:不顺手"改进"相邻代码/注释/格式,不重构没坏的东西,匹配现有风格(即使你更习惯另一种写法);发现无关死代码只口头提及,不删除
38
+ - 改动产生孤儿代码:删除因你改动而不再使用的 import / 变量 / 函数;不碰原有死代码,除非用户要求
46
39
 
47
40
  **检验标准:** 每一行改动都应该能追溯到用户的需求。
48
41
 
@@ -50,20 +43,7 @@ trigger: always_on
50
43
 
51
44
  **定义成功标准,循环直到验证通过。**
52
45
 
53
- 将命令式任务转换为可验证的目标:
54
-
55
- | 而非… | 转换为… |
56
- |--------|---------|
57
- | "加个校验" | "先写无效输入的测试,再让测试通过" |
58
- | "修这个 bug" | "先写能复现的测试,再修到测试通过" |
59
- | "重构 X" | "确保重构前后测试全部通过" |
60
-
61
- 多步任务先给简短计划:
62
- ```
63
- 1. [步骤] → 验证: [检查项]
64
- 2. [步骤] → 验证: [检查项]
65
- 3. [步骤] → 验证: [检查项]
66
- ```
46
+ 将命令式任务转换为可验证的目标——"加个校验"→"先写无效输入的测试再让通过";"修这个 bug"→"先写复现测试再修到通过";"重构 X"→"确保重构前后测试全部通过"。多步任务先给简短计划(步骤 → 验证项)。
67
47
 
68
48
  清晰的成功标准让 AI 可以独立循环验证,模糊的标准("搞一下")需要反复澄清。
69
49
 
@@ -92,13 +72,7 @@ trigger: always_on
92
72
 
93
73
  **任何实现动作前,先陈述方案并获得用户批准。**
94
74
 
95
- - 动手前说出打算怎么做(方案、涉及文件、验证方式),等用户确认
96
- - 仪式随任务缩放:小改动两三句话说清即可;大改动走 openspec 工作流(propose → specs → design)
97
- - "太简单不需要方案"是最危险的念头——简单意味着方案短,而不是没有方案
98
- - 中途发现复杂度超出预期:停下来重新说明,不要闷头扩大改动范围
99
-
100
- **检验标准:** 用户从未因"AI 直接改了代码没打招呼"而感到意外。
101
-
102
- ---
75
+ - 动手前说出打算怎么做(方案、涉及文件、验证方式),等用户确认;仪式随任务缩放:小改动两三句话说清,大改动走 openspec 工作流(propose → specs → design)
76
+ - "太简单不需要方案"是最危险的念头——简单意味着方案短,而不是没有方案;中途发现复杂度超出预期:停下来重新说明,不要闷头扩大改动范围
103
77
 
104
- **这些准则生效的标志:** diff 中不必要的改动减少、不会因过度设计而重写、澄清问题在实现前而非出错后提出。
78
+ **检验标准:** 用户从未因"AI 直接改了代码没打招呼"而感到意外。
@@ -8,7 +8,7 @@ trigger: always_on
8
8
  - 方法禁止超过 **100 行**,超过必须拆分为语义明确的子方法
9
9
  - 主方法只做步骤编排,涉及业务规则判断、状态变更或外部调用的循环/分支逻辑必须抽取为独立方法;仅做参数校验、数据组装、简单赋值等非业务逻辑可保留
10
10
  - 命名贴合业务含义,同一工程避免同名类,同一概念命名一致,禁止望文不知义的缩写(反例:`aeh` 表示"运费估算处理")
11
- - 类注释必须包含:说明、作者、版本、引入版本、相关类
11
+ - 类注释必须包含:说明、作者、相关类
12
12
  - 公共方法(public、REST、RPC)注释必须包含:入参要求和取值范围、出参结果、异常场景;私有/内部方法只要求写核心功能说明
13
13
  - 数据模型必须使用对应后缀:`Request`(入参)、`Response`(出参)、`DTO`(服务间传输)、`PO`(持久化对象)、`Param`(查询/操作参数)
14
14
 
@@ -28,7 +28,6 @@ trigger: always_on
28
28
  - 密码、密钥等敏感信息必须存储在 Lion 配置中心,禁止硬编码
29
29
  - 日志中禁止输出未脱敏的敏感信息
30
30
  - 后台敏感操作(删除/修改)必须记录审计日志:操作人、时间、操作内容,有请求上下文时记录来源 IP
31
- - 三方资源限制使用频率
32
31
 
33
32
  ## 五. 数据库规范
34
33
 
@@ -31,20 +31,13 @@ trigger: always_on
31
31
  2. **识别架构模式** — 确认项目使用的分包模式、命名约定、注解风格
32
32
  3. **保持一致性** — 新代码遵循项目风格,而非套用全局规范
33
33
 
34
- > 目录内风格不一致时,以目录内多数文件的风格为准;仍无法判断时,以项目显式规范为准。
35
-
36
34
  ### 冲突处理
37
35
 
38
36
  | 场景 | 处理方式 |
39
37
  |------|----------|
40
- | 项目已有分层架构 | 沿用现有分层,不强制统一为某种架构 |
41
- | 项目已有命名风格 | 保持统一,不自创新风格 |
42
- | 项目已用某中间件 | 继续使用,不强行切换 |
43
- | 项目内存在多种风格混用 | 以当前文件风格为准,不引入第三种风格;当前文件无明确风格时以目录内多数文件风格为准 |
44
- | 项目无先例可循(如首次使用 Redis、首次写 MQ 消费者) | 使用本规范库作为默认起点 |
45
- | 全局规范更新 | 以项目实际安装的版本为准,不自动覆盖 |
46
- | 全新项目 | 使用本规范库作为起点 |
47
- | 已有项目中的新模块 | 沿用项目现有约定,不另起风格 |
38
+ | 项目已有约定(分层架构 / 命名风格 / 中间件选型 / 新模块) | 一律沿用,不强制统一、不自创风格、不强行切换 |
39
+ | 项目内存在多种风格混用 | 以当前文件风格为准,不引入第三种风格;当前文件无明确风格时取目录内多数文件风格,仍有歧义以项目显式规范为准 |
40
+ | 项目无先例可循 / 全新项目(如首次使用 Redis、首次写 MQ 消费者) | 使用本规范库作为默认起点 |
48
41
 
49
42
  > **显式规范与代码风格冲突时的处理**:显式规范优先用于新代码;不要求对已有代码做大规模 reformat。判断标准:涉及命名约定、架构分层、中间件选型等**结构性决策**时以显式规范为准;仅涉及缩进、换行等**格式细节**时以当前文件风格为准。
50
43
 
@@ -4,35 +4,28 @@ trigger: always_on
4
4
 
5
5
  # 业务空间开发纪律(双模式,用户选择制)
6
6
 
7
- **仅当处于业务空间体系(空间根、`.worktrees/` 工作树、及其中各应用检出)时生效。空间支持两种开发模式,能力对称(都能修改 workspace 内容与应用代码、都强制分支隔离、交付均有 MR 与直合并两条通道,ADR 0007/0008),由用户显式选择,agent 只给推荐不代决。**
7
+ **仅当处于业务空间体系(空间根、`.worktrees/` 工作树、及其中各应用检出)时生效。空间支持本地模式 / worktree 模式两种开发模式,能力对称(ADR 0007/0008),由用户显式选择,agent 只给推荐不代决。执行细则以技能为唯一出处:本地模式 `cbb-local-mode`;worktree 全流程 `cbb-worktree-init` / `cbb-worktree-push` / `cbb-worktree-close`。**
8
8
 
9
- **模式一 · 本地模式(单线程快改,当前会话就地干):**
9
+ **模式一 · 本地模式(单线程快改,当前会话就地干):** 根 + 各涉及应用 `.codespace/<app>/` 就地切同名分支(基线 = `origin/<主干>`),当前会话原地开发,相对路径原样生效;**一次只承载一个需求**(独占本地检出;并行新需求走 worktree 模式,互不干扰)。收尾受下「收尾合并门禁」约束。
10
10
 
11
- - 就地切分支:根 + 各涉及应用 `.codespace/<app>/` 同名 `checkout -b`(基线 = `origin/<main>`,经 `cbb-local-mode` 技能执行)
12
- - 当前会话原地开发,相对路径原样生效;**一次只承载一个需求**(独占本地检出;期间并行新需求走 worktree 模式,互不干扰)
13
- - 收尾(收尾合并门禁,ADR 0007):`merge-branches.js --check` 判定已合并;未合并须三选一(提 MR / 直合并(临时 worktree 零接触,ADR 0008) / 确认废弃)后方可收尾;交付完成后各参与仓库回主干并删分支(`return-to-trunk.js` + `delete-branches.js`,主干名经远端 HEAD 探测,兼容 main / master)
14
-
15
- **模式二 · worktree 模式(并行 / 隔离):**
16
-
17
- - `cbb-worktree-init` 建隔离 worktree(AI 配置内容子目录已 junction 就位),**开发会话开进 `.worktrees/worktree-<需求名>/`**,可多需求并行;app 检出收拢在 `<wt>/.codespace/<app>/`——**镜像空间根布局**(`.codespace/<app>/...` 相对路径两模式通用,ADR 0003)
18
- - 根会话保留作控制面(worktree 生灭、`cbb update` 等空间级操作);**会话形态按工具定,能自动则 agent 亲自做**:OpenCode 桌面端 = agent 自动把会话切进/切出 worktree(`session_move`,上下文保留、不列入用户待办,空间根路径在案供回切);无会话搬移能力的桌面端 = 用户手动切根;CLI/TUI = 新开会话进 worktree
11
+ **模式二 · worktree 模式(并行 / 隔离):** `cbb-worktree-init` 建隔离 worktree,**开发会话开进 `.worktrees/worktree-<需求名>/`**,可多需求并行;app 检出收拢在 `<wt>/.codespace/<app>/`——**镜像空间根布局**(`.codespace/<app>/...` 相对路径两模式通用,ADR 0003)。根会话保留作控制面(worktree 生灭、`cbb update` 等空间级操作);会话切进/切出的工具差异与细则见 init / close 技能。
19
12
 
20
13
  **基准与防线(两模式共同,ADR 0002):**
21
14
 
22
- - **基准 = `origin/main`(唯一真相源);`.codespace/<app>/` 的检出 = 视图**——本地模式可暂态被需求分支占用,收尾恢复
23
- - **严禁直接在任何 main 检出上提交载荷**:分支感知 pre-commit hook 机械拦截(workspace 仓库仅放行控制面白名单:`CBB-WORKSPACE.json` / `.gitignore` / `AGENTS.md` / `openspec/config.yaml` / `openspec/schemas/**`;应用仓库 main 全拦;**merge 提交(`MERGE_HEAD` 存在)放行**,见 ADR 0007)——**被拦是防线生效,不是故障**;`--no-verify` 绕过仅限用户显式要求
24
- - 需求分支推送唯一出口 = `cbb-worktree-push`(init 创建流程零远端操作,lazy push);推送后可选提 MR 或**直接合并**(`merge-branches.js`,主干 / 测试分支,ADR 0008);收尾合并门禁同源(ADR 0007)
25
- - **收尾合并门禁(ADR 0007)**:两模式收尾均须每仓库"已合并"(`safety-check` / `merge-branches.js --check` 判定);未合并三选一——提 MR / 直合并 / 确认废弃(远程重命名 `<分支>-deprecated`);交付完成自动删本地+远程分支
26
- - **模式误判零代价**:本地模式中途要并行/隔离 → 重跑 `cbb-worktree-init`(分支已存在自动复用,提交随身带走)
15
+ - **基准 = `origin/<主干>`(唯一真相源);`.codespace/<app>/` 的检出 = 视图**——本地模式可暂态被需求分支占用,收尾恢复
16
+ - **严禁直接在任何主干检出上提交载荷**:分支感知 pre-commit hook 机械拦截(workspace 仓库仅放行控制面白名单:`CBB-WORKSPACE.json` / `.gitignore` / `AGENTS.md` / `openspec/config.yaml` / `openspec/schemas/**`;应用仓库主干全拦;**merge 提交(`MERGE_HEAD` 存在)放行**,见 ADR 0007)——**被拦是防线生效,不是故障**;`--no-verify` 绕过仅限用户显式要求
17
+ - 需求分支推送唯一出口 = `cbb-worktree-push`(init 创建流程零远端操作,lazy push);推送后可选提 MR 或**直接合并**(`merge-branches.js`,主干 / 测试分支,ADR 0008)
18
+ - **收尾合并门禁(ADR 0007)**:两模式收尾均须每仓库"已合并"(`safety-check` / `merge-branches.js --check` 判定);未合并三选一——提 MR / 直合并 / 确认废弃(远程重命名 `<分支>-deprecated`);交付完成回主干并自动删本地+远程分支,**废弃出口保留本地分支**(细则见 cbb-local-mode Step 5 / cbb-worktree-close Step 3.4)
19
+ - **模式误判零代价**:本地模式中途要并行/隔离 → 重跑 `cbb-worktree-init`——并行新需求直接建(不占本地检出、互不干扰);升级当前需求先回主干腾检出后重跑(分支自动复用、提交随身带走)
27
20
 
28
- **五时刻对齐(agent 必须遵守):**
21
+ **五时刻对齐(agent 必须遵守;1–3 的细则见 `cbb-local-mode` 技能):**
29
22
 
30
- 1. **开工前——状态自检**(`cbb-local-mode` 的 `check-local-state.js`):干净态零交互直接开工;**遗留态(别的需求未收尾,任一仓库检出在非主干分支)必须让用户四选一**(继续 / 先收尾 / 升级 worktree / 丢弃),不得自行决定
23
+ 1. **开工前——状态自检**:干净态零交互直接开工;**遗留态(别的需求未收尾,任一仓库检出在非主干分支)必须让用户四选一**(继续 / 先收尾 / 升级 worktree / 丢弃),不得自行决定
31
24
  2. **开工时——布局确认**:向用户展示参与仓库与分支名,确认后执行
32
25
  3. **进行中——插单归属**:用户插入非本需求的改动,先确认归属(并入 / 收尾后处理 / 转 worktree),不得静默混入
33
- 4. **打包前——主干对齐**(任何构建打包动作前):对产物所涉及的仓库(按代码来源判定:哪个仓库的代码被编进产物就同步哪个,与产物存放位置无关;多仓聚合产物则全部同步)先对齐再打包——① `git fetch <远端>`;② 在当前需求分支执行 `git merge <远端>/<主干>`(远端通常为 `origin`,主干名以远端 HEAD 为准,常见 main / master;在 main 检出上打包时改为 `git pull`;工作区未提交修改阻碍合并时,先提交或暂存再对齐);③ 冲突未解决时**禁止打包**,停下报告用户;fetch/merge 失败 → 报告并等待用户指示;④ 对齐完成后(由此产生的 merge 提交属正常)再执行打包命令(`mvn package` / `npm run build` / 前端 bundle / 生成部署包等凡产出构建产物者);产物目录须已被 `.gitignore` 忽略(未忽略先补 ignore——该修改属普通变更,随需求分支提交)。产物基线 = 主干最新,避免基于过时分支打包导致的集成返工;**不 rebase、不 force push**,merge 提交随下次 push 一并推送
34
- 5. **打包后——产物收集与留存**:打包完成后把各仓库产物**复制**到 `<空间根>/.build/<需求名>/<应用名>/`(原件保持原位;需求名 = 分支名;workspace 仓库产物用 `workspace` 子目录;目标目录已存在时先向用户确认清理或覆盖;`.build/` 由 cbb 写入空间根 `.gitignore`)——产物按需求聚合留存,worktree 关闭后不丢失;打包时顺带报告 `.build/` 积压现状(需求目录数与总体积),建议清理已交付或已废弃需求的产物目录,经用户逐项确认后执行、**永不自动删除**
26
+ 4. **打包前——主干对齐**(任何构建打包动作前):产物涉及哪些仓库(按代码来源判定,与产物存放位置无关;多仓聚合则全部)就先同步哪些——① `git fetch <远端>`;② 在当前需求分支 `git merge <远端>/<主干>`(主干名以远端 HEAD 为准;在主干检出上打包改为 `git pull`;未提交修改阻碍合并时先提交或暂存);③ 冲突未解决**禁止打包**,停下报告用户;fetch/merge 失败 → 报告并等待指示;④ 对齐完成(merge 提交属正常)再打包;产物目录须已被 `.gitignore` 忽略(未忽略先补,随需求分支提交)。产物基线 = 主干最新;**不 rebase、不 force push**,merge 提交随下次 push 一并推送
27
+ 5. **打包后——产物收集与留存**:把各仓库产物**复制**到 `<空间根>/.build/<需求名>/<应用名>/`(原件原位;需求名 = 分支名;workspace 用 `workspace` 子目录;目标已存在先向用户确认清理或覆盖;`.build/` 由 cbb 写入空间根 `.gitignore`)——按需求聚合留存,worktree 关闭后不丢失;打包时顺带报告 `.build/` 积压现状(需求目录数与总体积),建议清理已交付/废弃需求的产物目录,经用户逐项确认后执行、**永不自动删除**
35
28
 
36
- **读基准代码约定:** 本地模式进行中,工作区是开发态而非基准——要读"main 现状"先确认检出分支,或直接 `git show main:<path>` / `git diff main`;不要默认工作区即基准。
29
+ **读基准代码约定:** 本地模式进行中,工作区是开发态而非基准——读"主干现状"须从主干显式获取;不要默认工作区即基准(细则见 `cbb-local-mode` 技能)。
37
30
 
38
- **检验标准:** 任何 main 检出上无载荷提交(hook 兜底,git log 可机械审计;merge 提交除外);本地模式收尾后全部仓库回主干且 clean;worktree 模式的**变更(代码改动)**只出现在需求工作树内(打包产物按第 5 条收集到空间根 `.build/`,不在此限);需求分支只经 push 技能产生;**未合并且非废弃的分支无法通过收尾(门禁兜底)**。
31
+ **检验标准:** 任何主干检出上无载荷提交(hook 兜底,git log 可机械审计;merge 提交除外);本地模式收尾后全部仓库回主干且 clean;worktree 模式的变更(代码改动)只出现在需求工作树内(打包产物按第 5 条收集到 `.build/`,不在此限);需求分支只经 push 技能产生;**未合并且非废弃的分支无法通过收尾(门禁兜底)**。
@@ -289,6 +289,7 @@ public interface OrderConverter {
289
289
  - 二方、三方调用必须通过防腐层(Repo),面向失败编程,考虑网络抖动、服务劣化等异常
290
290
  - 所有二方、三方调用必须明确设置超时时间
291
291
  - 三方调用发生错误时,对错误响应编码分类打点,必须打印日志
292
+ - 第三方资源有限频约束:调用前确认频率上限(QPS / 日配额),以缓存 / 批量合并 / 异步削峰控制调用量;接近或超限时必须有降级路径
292
293
 
293
294
  #### IO 优化
294
295
 
@@ -129,12 +129,12 @@ worktree 需要知道要关联哪些应用仓库。
129
129
  ]
130
130
  }
131
131
  ```
132
- - 写入成功后提交到当前分支(确保 Step 2 状态检查通过):
132
+ - 写入后提交到当前分支(Step 2 状态检查与根检出分支无关,本地模式进行中直接提交即可):
133
133
  ```bash
134
134
  git add CBB-WORKSPACE.json
135
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,否则无法确定关联应用。
@@ -181,17 +181,18 @@ node "<skill_dir>/scripts/sync-repos.js" "<ws_root>"
181
181
 
182
182
  ## Step 2:状态检查
183
183
 
184
- 对工作空间和关联应用仓库执行**分级检查**——workspace 仓库标准检查(`.gitignore` 操作由 Step 3.2 的 stash 机制保护),app 仓库轻量检查(仅需确保 `origin/main` 引用可用)。**全部通过才继续,任一失败则终止并报告所有问题**(不 fail-fast,跑完所有仓库再汇总)。
184
+ 对工作空间和关联应用仓库执行**统一轻量检查**——路径/仓库形态/`git fetch`/`origin/main` 引用可用,workspace 另加 HEAD 非 detached(update-gitignore 的回切依赖分支名)。检查与根检出分支状态解耦(分支基线恒为 `origin/<main>`,本地模式进行中根检出在任何分支均可通过;`.gitignore` 操作由 Step 3.2 的 stash 机制保护)。**全部通过才继续,任一失败则终止并报告所有问题**(不 fail-fast,跑完所有仓库再汇总)。
185
185
 
186
186
  | 检查项 | workspace 仓库 | app 仓库 |
187
187
  |--------|:---:|:---:|
188
188
  | 路径存在且是 git 仓库 | ✅ | ✅ |
189
189
  | 不在 worktree 中(排除 submodule) | ✅ | ✅ |
190
190
  | 不是 submodule | ✅ | ✅ |
191
+ | HEAD 非 detached | ✅ | ❌(无 checkout 动作) |
191
192
  | `git fetch` 拉取远程最新 | ✅ | ✅ |
192
- | 本地主分支已最新 | ✅ | ❌(直接从 `origin/main` 创建分支) |
193
+ | `origin/main` 引用可用 | ✅ | ✅ |
193
194
  | 无未提交修改 | ❌(由 Step 3.2 stash 处理) | ❌ |
194
- | 当前在主分支上 | ✅ | ❌ |
195
+ | 当前在主分支上 | ❌(本地模式进行中仍可创建) | ❌ |
195
196
 
196
197
  ```bash
197
198
  node "<skill_dir>/scripts/check-repos.js" "<ws_root>"
@@ -199,7 +200,7 @@ node "<skill_dir>/scripts/check-repos.js" "<ws_root>"
199
200
 
200
201
  **解读输出**:
201
202
  - 退出码 0 + 末行 `CHECK_RESULT ALL_OK` → 全部通过,继续 Step 3
202
- - 退出码 2 + 末行 `CHECK_RESULT HAS_FAILURES` → 部分失败,AI agent 报告上面列出的所有问题,**终止流程**
203
+ - 退出码 2 + 末行 `CHECK_RESULT HAS_FAILURES` → 部分失败,AI agent 报告上面列出的所有问题,**终止流程**(若报 `detached HEAD` → 提示用户先 `git checkout <分支>` 挂回某分支再重跑)
203
204
 
204
205
  ---
205
206
 
@@ -228,15 +229,17 @@ node "<skill_dir>/scripts/check-repos.js" "<ws_root>"
228
229
  node "<skill_dir>/scripts/update-gitignore.js" "<ws_root>" "<branch>"
229
230
  ```
230
231
 
231
- 脚本内部自动完成:stash 保护 → 分支不存在则从 HEAD 创建 → 切分支 → 添加 `.worktrees/` 和 `.codespace/` → 提交 → 切回 → pop stash
232
+ 脚本内部自动完成:同名升级早退 → ensure 本地 `.git/info/exclude` 兜底(在 stash 前)→ stash 保护(`-u` 含 untracked)→ 分支不存在则从 `<remote>/<main>` 创建 → 切分支 → 添加 `.worktrees/` 和 `.codespace/` → 提交 → 切回原分支 → pop stash
232
233
 
233
234
  **解读输出**:
234
235
  - 退出码 0 + 末行 `GITIGNORE_OK` → 成功
235
- - 退出码 2 → 执行失败,**终止流程**
236
+ - 退出码 2 → 执行失败,**终止流程**(含**同名升级早退**:目标分支正被根检出占用——本地模式同名升级 worktree 场景,零副作用退出并提示先收尾本地模式或把根检出切回主干,主干名以远端 HEAD 为准、勿写死 main)
236
237
 
237
- > ⓘ **分支自动创建**:本步骤先于 Step 3.3 执行,workspace 分支此时不存在,脚本会自动从 HEAD 创建(与 `create-branches.js` 对 workspace 的行为一致,Step 3.3 报 `reused`)。`.gitignore` 提交正好落在需求分支上,由 Step 3.4 push 一并推送。
238
+ > ⓘ **分支自动创建**:本步骤先于 Step 3.3 执行,workspace 分支此时不存在,脚本会自动从 `<remote>/<main>` 创建(与 `create-branches.js` 对 workspace 的行为一致,Step 3.3 报 `reused`)。基线不取 HEAD——本地模式进行中重跑本技能,根检出在任何分支/有未提交修改均不被污染。`.gitignore` 提交落在需求分支上,由后续 `cbb-worktree-push` 首次推送一并带入。
238
239
 
239
- > ⚠️ 必须在提交后切回主分支,否则 Step 3.5 的 `git worktree add` 会因分支已检出而失败(脚本已自动处理)。
240
+ > ⓘ **info/exclude 本地兜底**:脚本在 stash **之前**把 `.worktrees/`、`.codespace/` 幂等写入本地 `.git/info/exclude`——先忽略两个目录,`stash -u` 才不会把 `.codespace/`(应用全量检出)一并吞入;同时 gitignore 条目在需求分支合并前,根检出(其他分支)仍缺条目、目录显示 untracked(存量空间噪音),exclude 与分支无关、即时消噪。团队层条目仍走 .gitignore 提交(检测按文件内容,不受 exclude 干扰)。
241
+
242
+ > ⚠️ 必须在提交后切回**原检出分支**(脚本已自动处理,含本地模式的需求分支),否则 Step 3.5 的 `git worktree add` 会因目标分支仍被根检出占用而失败。
240
243
 
241
244
  ### 3.3 创建分支
242
245
 
@@ -367,8 +370,8 @@ node "<skill_dir>/scripts/junction-ai-config.js" "<ws_root>" "<wt_path>"
367
370
  | 空间新增了应用(编辑 `CBB-WORKSPACE.json` 后) | Step 1 拉取新应用代码 → Step 3.3 为新应用建分支 → Step 3.5 在 `.codespace/` 容器内创建缺失的 app worktree → Step 3.6 补 junction |
368
371
  | 上次 init 中途失败 | 已存在的分支 / worktree 自动复用,`.gitignore` 处理自动跳过,仅补齐缺失部分 |
369
372
  | 应用从 config 移除 | 已创建的分支 / worktree **不会**自动删除;如需清理用 `cbb-worktree-close` skill 关闭后重建 |
370
- | **本地模式升级 worktree**(ADR 0002) | Step 3.3 分支已存在 → reused(本地阶段提交随身带走);先把各仓库本地检出切回主干(主干名以远端 HEAD 为准,勿写死 main),再重跑本技能 |
371
- | 本地模式进行中重跑 | 安全:分支基线取 `origin/main`(非 HEAD),不受根/应用检出状态影响;fetch 不动工作区 |
373
+ | **本地模式升级 worktree**(ADR 0002) | Step 3.2 检测到根检出即目标分支 → **零副作用早退**并提示先把各仓库本地检出切回主干(主干名以远端 HEAD 为准,勿写死 main)后重跑;切回后重跑:Step 3.3 分支已存在 → reused(本地阶段提交随身带走) |
374
+ | 本地模式进行中重跑 | 安全:分支基线取 `origin/main`(非 HEAD),不受根/应用检出状态影响;fetch 不动工作区;根检出在任何分支均可通过 Step 2 |
372
375
 
373
376
  > ⓘ 本技能**零远端副作用**(lazy push):不存在"重跑推送开发中提交"的风险面——推送只发生在 `cbb-worktree-push`。
374
377
 
@@ -378,9 +381,9 @@ node "<skill_dir>/scripts/junction-ai-config.js" "<ws_root>" "<wt_path>"
378
381
 
379
382
  | 场景 | 处理方式 |
380
383
  |------|----------|
381
- | `.worktrees/` / `.codespace/` 未在 `.gitignore` | `cbb setup` / `cbb update` 已写入(0.0.3-beta.22 起含 `.worktrees/`);存量空间由 Step 3.2 补写提交 |
384
+ | `.worktrees/` / `.codespace/` 未在 `.gitignore` | `cbb setup` / `cbb update` 已写入(0.0.3-beta.22 起含 `.worktrees/`);存量空间由 Step 3.2 补写提交(同时幂等写入本地 `.git/info/exclude` 兜底消噪) |
382
385
  | `git worktree add` 权限被拒 | 运行环境降级,中断并告知用户 |
383
- | 状态检查任一失败 | 终止并报告所有问题 |
386
+ | 状态检查任一失败 | 终止并报告所有问题(`detached HEAD` → 提示用户先 `git checkout <分支>`) |
384
387
  | 部分应用 worktree 创建失败 | 提示已创建的回滚清理命令 |
385
388
  | workspace 主分支 tracked 内容(openspec/、cbb/ 等) | 自动 checkout 到工作树,无需额外动作(cp/clone 都是多余的) |
386
389
  | AI 配置目录(.claude/、.qoder/、.opencode/ 等) | Step 3.6 junction 内容子目录进工作树;唯一物理副本在空间根,`cbb update` 一处生效 |
@@ -400,10 +403,10 @@ node "<skill_dir>/scripts/junction-ai-config.js" "<ws_root>" "<wt_path>"
400
403
  - **问题:** 直接在代码中写死仓库 URL 或本地路径,不同团队的仓库地址不同
401
404
  - **修复:** 始终从 `CBB-WORKSPACE.json` 读取应用与远程仓库的映射;配置缺失/为空时按 Step 0.3 配置引导处理,不支持在流程中临时指定配置外的仓库
402
405
 
403
- ### 状态检查对 app 仓库执行完整五项检查
406
+ ### 状态检查残留旧基线约束(要求工作区在主干 / 主分支已最新)
404
407
 
405
- - **问题:** 对所有仓库一视同仁执行全部五项检查(含"当前在主分支""无未提交修改"),导致开发者在 feature 分支上或有 WIP 时无法创建新 worktree
406
- - **修复:** Step 2 采用分级检查——workspace 仓库完整检查,app 仓库仅轻量检查(路径有效 + fetch 成功 + `origin/main` 可用)
408
+ - **问题:** 分支基线取 HEAD 的时代要求「当前在主分支」「主分支已最新」,否则拦截;但自 ADR 0002 起所有仓库分支统一从 `origin/main` 创建,与本地检出状态完全解耦——本地模式进行中(根检出在需求分支)被误拦,且「本地 main 已最新」实际检测的是 HEAD 落后(语义错误)
409
+ - **修复:** Step 2 对 workspace 与 app 统一轻量检查(路径 / 仓库形态 / fetch + `origin/main` 引用可用;workspace 另加 HEAD 非 detached);同名升级场景(目标分支正被根检出占用)的强制点前移至 Step 3.2 零副作用早退——不再有「本地模式进行中不能创建 worktree」的误拦
407
410
 
408
411
  ### 状态检查失败后继续执行
409
412
 
@@ -413,17 +416,17 @@ node "<skill_dir>/scripts/junction-ai-config.js" "<ws_root>" "<wt_path>"
413
416
  ### 忽略 .gitignore 安全验证
414
417
 
415
418
  - **问题:** `.worktrees/` 或 `.codespace/` 目录未被忽略,工作树或代码目录内容被 git 跟踪
416
- - **修复:** 创建工作树前必须用 `git check-ignore` 验证两者均已忽略
419
+ - **修复:** 创建工作树前必须验证两者已忽略——`.gitignore` 条目按文件内容检测并提交(团队层);同时幂等写入 `.git/info/exclude`(本地跨分支兜底,消除根检出在条目合并前的 untracked 噪音)
417
420
 
418
421
  ### 未先创建分支就直接创建 worktree
419
422
 
420
423
  - **问题:** 直接用 `git worktree add -b` 同时创建分支和 worktree,导致部分仓库分支创建成功但 worktree 失败时状态不一致
421
- - **修复:** 严格按 Step 3 先统一创建分支(3.3)、再统一创建 worktree(3.4)的顺序执行
424
+ - **修复:** 严格按 Step 3 先统一创建分支(3.3)、再统一创建 worktree(3.5)的顺序执行
422
425
 
423
426
  ### 用 checkout -b 创建分支后再创建 worktree
424
427
 
425
428
  - **问题:** `git checkout -b <需求名>` 将主目录切到新分支,随后 `git worktree add .worktrees/worktree-<需求名> <需求名>` 报 `fatal: '<需求名>' is already checked out`
426
- - **修复:** Step 3.3 只用 `git branch` 创建分支,不切换;分支检出由 Step 3.4 的 worktree 完成
429
+ - **修复:** Step 3.3 只用 `git branch` 创建分支,不切换;分支检出由 Step 3.5 的 worktree 完成
427
430
 
428
431
  ### 把 worktree 内容当作"软链/克隆"处理
429
432
 
@@ -474,7 +477,8 @@ node "<skill_dir>/scripts/junction-ai-config.js" "<ws_root>" "<wt_path>"
474
477
 
475
478
  **必须执行**
476
479
 
477
- - workspace 仓库标准检查(当前在主分支等),app 仓库轻量检查(fetch + `origin/main` 可用即可)
480
+ - workspace 与 app 仓库统一轻量检查(fetch + `origin/main` 引用可用即可;workspace 另加 HEAD 非 detached)——**不要求在主分支 / 本地 main 最新**:分支基线恒为 `origin/<main>`,本地模式进行中根检出在任何分支均可创建 worktree
481
+ - 同名升级场景强制点:目标分支正被根检出占用时,Step 3.2(update-gitignore)零副作用早退——禁止绕过早退强行创建
478
482
  - 所有仓库分支统一从 `origin/<main>` 创建(`git branch <需求名> ${REMOTE}/${MAIN}`),不依赖本地检出状态(本地模式进行中重跑不受污染)
479
483
  - 严格 Step 3.3 → Step 3.5 顺序(先统一创建分支,再统一创建 worktree;workspace 工作树先建,各应用按序补建)
480
484
  - Step 3.6 junction AI 配置(失败不阻塞,但要报告)
@@ -490,8 +494,8 @@ node "<skill_dir>/scripts/junction-ai-config.js" "<ws_root>" "<wt_path>"
490
494
  | `check-env.js` | 环境探活 | 无 | 0/1/2 |
491
495
  | `parse-config.js` | 解析 CBB-WORKSPACE.json | 无 | 0/1/2 |
492
496
  | `sync-repos.js` | 同步 .codespace/ 仓库(兜底 clone 时补装提交防线 hook) | ws_root | 0/1/2 |
493
- | `check-repos.js` | 状态检查 | ws_root | 0/1/2 |
494
- | `update-gitignore.js` | 安全更新 .gitignore | ws_root, branch | 0/1/2 |
497
+ | `check-repos.js` | 状态检查(统一轻量 + workspace detached 附加项) | ws_root | 0/1/2 |
498
+ | `update-gitignore.js` | 安全更新 .gitignore(同名升级早退 + info/exclude 兜底) | ws_root, branch | 0/1/2 |
495
499
  | `create-branches.js` | 创建分支(统一从 origin/<main> 建) | ws_root, branch, [--apps] | 0/1/2 |
496
500
  | `create-worktrees.js` | 创建 worktree(仅 prune 元数据,不删物理目录) | ws_root, branch, [--apps] | 0/1/2/3 |
497
501
  | `junction-ai-config.js` | AI 配置内容子目录 junction 进工作树(ADR 0002 防线 B) | ws_root, wt_path | 0/1/2 |
@@ -6,17 +6,19 @@ require('./silence-popup');
6
6
  * 用法:node check-repos.js <ws_root>
7
7
  *
8
8
  * 检查项:
9
- * workspace 仓库(完整检查 5 项):
9
+ * workspace 仓库(轻量检查 + detached 附加项):
10
10
  * \u2460 路径存在且是 git 仓库
11
11
  * \u2461 不在 worktree 中(排除 submodule)
12
12
  * \u2462 不是 submodule
13
- * \u2463 git fetch 拉取远程最新
14
- * \u2464 本地主分支已最新 + 当前在主分支
13
+ * \u2463 HEAD 非 detached(仅 workspace;update-gitignore 回切依赖分支名)
14
+ * \u2464 git fetch + origin/main 引用可用
15
15
  *
16
- * app 仓库(轻量检查 3 项):
17
- * \u2460 路径存在且是 git 仓库
18
- * \u2461 不在 worktree 中(排除 submodule)
19
- * \u2462 git fetch + origin/main 引用可用
16
+ * app 仓库(轻量检查 4 项):同 workspace 但无 detached 附加项
17
+ *
18
+ * 历史说明:workspace 曾要求「当前在主分支」「本地主分支已最新」——那是分支基线取
19
+ * HEAD 时代的约束;自 ADR 0002 起所有仓库分支统一从 origin/<main> 创建,与根检出
20
+ * 状态完全解耦(本地模式进行中根检出在任何分支均可创建 worktree),该两项检查已移除。
21
+ * 同名升级场景(目标分支正被根检出占用)的强制点移至 update-gitignore.js 的零副作用早退。
20
22
  *
21
23
  * 退出码:
22
24
  * 0 - 全部通过
@@ -106,6 +108,16 @@ function checkRepo(repoPath, name, level) {
106
108
  problems.push("is git submodule");
107
109
  }
108
110
 
111
+ // 4.5: workspace 附加项 —— HEAD 必须挂在分支上(非 detached)。
112
+ // update-gitignore.js 的回切依赖分支名(origBranch),detached 下 origBranch 为
113
+ // "HEAD",回切会停到新分支 tip、丢失原位置。app 仓库无 checkout 动作,无需此项。
114
+ if (level === "workspace") {
115
+ const symRef = tryExec("git symbolic-ref -q HEAD", repoPath);
116
+ if (!symRef.ok) {
117
+ problems.push("detached HEAD (run git checkout <branch> first)");
118
+ }
119
+ }
120
+
109
121
  // 5: fetch
110
122
  const remote = tryExec("git remote", repoPath);
111
123
  if (!remote.ok || !remote.out) {
@@ -115,22 +127,9 @@ function checkRepo(repoPath, name, level) {
115
127
  const fetch = tryExec("git fetch -q \"" + r + "\"", repoPath);
116
128
  if (!fetch.ok) {
117
129
  problems.push("git fetch failed");
118
- } else if (level === "workspace") {
119
- const main = detectMainBranch(repoPath);
120
- if (!main) {
121
- problems.push("cannot detect main branch");
122
- } else {
123
- const behind = tryExec("git log HEAD..\"" + r + "/" + main + "\" --oneline", repoPath);
124
- if (behind.ok && behind.out) {
125
- problems.push("main branch has unpulled commits");
126
- }
127
- const current = tryExec("git rev-parse --abbrev-ref HEAD", repoPath);
128
- if (current.ok && current.out !== main) {
129
- problems.push("not on main branch (current " + current.out + ", expected " + main + ")");
130
- }
131
- }
132
130
  } else {
133
- // app lightweight check: origin/main available
131
+ // 轻量检查(workspace 与 app 一致):origin/<main> 引用可用即可。
132
+ // 分支基线恒为 origin/<main>(ADR 0002),不依赖本地检出/本地主分支状态。
134
133
  const main = detectMainBranch(repoPath);
135
134
  const refCheck = tryExec("git rev-parse --verify --quiet \"refs/remotes/" + r + "/" + main + "\"", repoPath);
136
135
  if (!refCheck.ok) {
@@ -104,6 +104,14 @@ if (fs.existsSync(wtDir)) {
104
104
  if (!created.ok) {
105
105
  console.error('❌ workspace worktree 创建失败:' + created.err);
106
106
  console.error(' 常见原因:分支已在其他 worktree 检出,或权限不足');
107
+ // 同名升级兜底识别:根检出当前分支 == 目标分支(不依赖 git 错误消息文本——
108
+ // 新老版本分别报 "already used by worktree" / "already checked out at")。
109
+ // 正常流程会被 Step 3.2 早退拦截,此处防绕过/直调脚本场景,给出针对性指引。
110
+ const curBranch = run('git rev-parse --abbrev-ref HEAD', wsRoot);
111
+ if (curBranch.ok && curBranch.out === branch) {
112
+ console.error(' ℹ️ 分支 ' + branch + ' 正被根检出占用(本地模式同名升级场景):先收尾本地模式');
113
+ console.error(' 或把根检出切回主干(主干名以远端 HEAD 为准,勿写死 main)后重跑本技能。');
114
+ }
107
115
  process.exit(2);
108
116
  }
109
117
  console.log('✅ workspace worktree 已创建:' + wtDir);
@@ -5,22 +5,30 @@ require('./silence-popup');
5
5
  *
6
6
  * 用法:node update-gitignore.js <ws_root> <branch>
7
7
  *
8
- * 行为:
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
- *
17
- * 幂等:目标分支已在某个 worktree 中检出(init-worktree 重跑场景,上次已处理过
18
- * .gitignore)时,跳过全部操作直接输出 GITIGNORE_OK——避免 checkout 与 worktree 检出冲突。
8
+ * 行为(编号与代码内注释一致):
9
+ * 1. 检测当前分支
10
+ * 1.5 同名升级早退:目标分支正被根检出占用(origBranch == branch,本地模式升级
11
+ * worktree 场景)→ 零副作用退出(exit 2)——worktree add 必然失败(分支不能
12
+ * 双检出),且 checkout/commit 会打开无谓的副作用窗口;提示先收尾本地模式或
13
+ * 切回主干。detached 由 check-repos.js 前置拦截。
14
+ * 1.1 幂等检查:目标分支已在某 worktree 检出(init-worktree 重跑场景,上次已处理过
15
+ * .gitignore)时,跳过全部操作直接输出 GITIGNORE_OK——避免 checkout 与 worktree 检出冲突。
16
+ * 1.7 本地兜底:先把 .worktrees/ 与 .codespace/ ensure 进 .git/info/exclude(须在
17
+ * stash -u 之前——否则未忽略的 .codespace/ 大目录会被吞入 stash;同时消除
18
+ * 「.gitignore 条目在需求分支合并前、根检出仍显示 untracked」的存量空间噪音。跨分支即时生效、幂等)
19
+ * 2. stash 当前未提交修改(-u 含 untracked;仅 untracked 时不产生 stash)
20
+ * 2.1 目标分支不存在则从 <remote>/<main> 创建(首跑时分支尚未由 create-branches.js 建立,
21
+ * 顺序无关自愈;create-branches.js 后续会报 reused;基线不取 HEAD,见 ADR 0002)
22
+ * 3. 切换到目标分支
23
+ * 4. 检查 .worktrees/ 和 .codespace/ 是否在 .gitignore,缺失则添加并提交
24
+ * (检测按文件内容——1.7 的 info/exclude 兜底会让 git check-ignore 误报"已忽略")
25
+ * 5. 切回原分支
26
+ * 6. pop stash(如有)
19
27
  *
20
28
  * 退出码:
21
29
  * 0 - 成功
22
30
  * 1 - 参数错误
23
- * 2 - 执行失败
31
+ * 2 - 执行失败(含同名升级早退)
24
32
  */
25
33
 
26
34
  const { execSync } = require('child_process');
@@ -51,6 +59,16 @@ if (!origBranch.ok) {
51
59
  }
52
60
  console.log('当前分支:' + origBranch.out);
53
61
 
62
+ // 1.5 同名升级早退:目标分支正被根检出占用(本地模式同名升级场景)。
63
+ // 此时 worktree add 必然失败(同一分支不能双检出),且后续 checkout/commit 打开无谓的
64
+ // 副作用窗口——直接零副作用退出,提示先收尾本地模式或切回主干(check-repos 轻量化后的唯一强制点)。
65
+ // 注意:detached HEAD 由 check-repos.js 拦截(origBranch 会是 "HEAD"),此处只需比对分支名。
66
+ if (origBranch.out === branch) {
67
+ console.error('❌ 目标分支 ' + branch + ' 正被根检出占用(本地模式同名升级场景)。');
68
+ console.error(' 请先收尾本地模式,或把根检出切回主干(主干名以远端 HEAD 为准,勿写死 main)后重跑本技能。');
69
+ process.exit(2);
70
+ }
71
+
54
72
  // 1.1 幂等检查:目标分支已被某 worktree 检出 → 上次 init-worktree 已处理 .gitignore
55
73
  const wtList = run('git worktree list --porcelain', wsRoot);
56
74
  const branchInWorktree = wtList.ok && wtList.out.split(/\r?\n/).some(l => l.trim() === 'branch refs/heads/' + branch);
@@ -61,11 +79,36 @@ if (branchInWorktree) {
61
79
  process.exit(0);
62
80
  }
63
81
 
64
- // 2. stash 当前未提交修改
82
+ // 1.7 本地兜底:先把 .worktrees/ 与 .codespace/ ensure 进 .git/info/exclude。
83
+ // 必须在 stash -u 之前:让两个目录先被忽略,否则 -u 会把未忽略的 .codespace/
84
+ // 大目录(应用全量检出)一并吞入 stash——慢且有滞留风险。同时兜底消除
85
+ // 「.gitignore 条目在需求分支合并前、根检出仍显示 untracked」的存量空间噪音。
86
+ // exclude 与分支无关、本地即时生效、幂等;团队层条目仍走第 4 步的 .gitignore 提交。
87
+ const excludeRel = run('git rev-parse --git-path info/exclude', wsRoot);
88
+ if (excludeRel.ok && excludeRel.out) {
89
+ const excludePath = path.resolve(wsRoot, excludeRel.out);
90
+ fs.mkdirSync(path.dirname(excludePath), { recursive: true });
91
+ let exContent = fs.existsSync(excludePath) ? fs.readFileSync(excludePath, 'utf-8') : '';
92
+ const exLines = exContent.split(/\r?\n/).map(l => l.trim());
93
+ const exMissing = ['.worktrees/', '.codespace/']
94
+ .filter(d => !exLines.includes(d) && !exLines.includes(d.replace(/\/$/, '')));
95
+ if (exMissing.length > 0) {
96
+ if (exContent && !exContent.endsWith('\n')) exContent += '\n';
97
+ fs.writeFileSync(excludePath, exContent + exMissing.join('\n') + '\n');
98
+ console.log('✅ 已写入 .git/info/exclude(跨分支兜底):' + exMissing.join(', '));
99
+ } else {
100
+ console.log('ℹ️ .git/info/exclude 兜底条目已存在,跳过');
101
+ }
102
+ } else {
103
+ console.log('ℹ️ 无法定位 info/exclude,跳过本地兜底(不影响主流程)');
104
+ }
105
+
106
+ // 2. stash 当前未提交修改(-u 含 untracked:防 checkout 被「untracked 与目标分支
107
+ // tracked 同名」拒绝;-u 前已 ensure exclude,.worktrees/ / .codespace/ 不会被吞)
65
108
  let stashed = false;
66
109
  const status = run('git status --porcelain', wsRoot);
67
110
  if (status.ok && status.out) {
68
- const stash = run('git stash push -m "init-worktree-temp"', wsRoot);
111
+ const stash = run('git stash push -u -m "init-worktree-temp"', wsRoot);
69
112
  if (stash.ok) {
70
113
  if (/No local changes to save/.test(stash.out)) {
71
114
  console.log('ℹ️ 仅 untracked 文件,无需 stash');
@@ -115,15 +158,16 @@ if (!checkout.ok) {
115
158
  console.log('✅ 切换到分支:' + branch);
116
159
 
117
160
  // 4. 检查并添加 .worktrees/ 和 .codespace/ 到 .gitignore
161
+ // 检测用文件内容(而非 git check-ignore——1.7 的 info/exclude 兜底会让 check-ignore
162
+ // 报告"已忽略",从而永远跳过团队层 .gitignore 提交);此处判断的是 tracked 文件自身条目。
118
163
  const gitignorePath = path.join(wsRoot, '.gitignore');
119
164
  let content = fs.existsSync(gitignorePath) ? fs.readFileSync(gitignorePath, 'utf-8') : '';
120
- const original = content;
121
165
  const needCommit = [];
122
166
 
123
167
  for (const dir of ['.worktrees/', '.codespace/']) {
124
- const check = run('git check-ignore -q "' + dir + '"', wsRoot);
125
- if (!check.ok) {
126
- // 未忽略:添加
168
+ const lines = content.split(/\r?\n/).map(l => l.trim());
169
+ const hasEntry = lines.includes(dir) || lines.includes(dir.replace(/\/$/, ''));
170
+ if (!hasEntry) {
127
171
  if (content && !content.endsWith('\n')) content += '\n';
128
172
  content += dir + '\n';
129
173
  needCommit.push(dir);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jspg-ai/coding-bb",
3
- "version": "0.0.5-beta.4",
3
+ "version": "0.0.5-beta.6",
4
4
  "description": "整合业界热门且高价值的工具、框架与技能,为 AI CODING AGENT 提供统一的行为准则与工作流,辅助开发者将需求高效落地为符合规范的代码",
5
5
  "main": "cbb/lib/install/init.js",
6
6
  "bin": {