@jspg-ai/coding-bb 0.0.3-beta.3 → 0.0.3-beta.31

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.
Files changed (145) hide show
  1. package/cbb/dev-standards/rules/cbb-ai-behavior.md +135 -116
  2. package/cbb/lib/external/index.js +228 -0
  3. package/cbb/lib/install/agents-nav.js +92 -0
  4. package/cbb/lib/install/claude-code.js +7 -2
  5. package/cbb/lib/install/command-skill.js +105 -0
  6. package/cbb/lib/install/init.js +502 -168
  7. package/cbb/lib/install/minimax.js +60 -0
  8. package/cbb/lib/install/opencode.js +3 -5
  9. package/cbb/lib/install/qoder.js +1 -3
  10. package/cbb/lib/install/rule-rewrite.js +27 -0
  11. package/cbb/lib/install/setup-decision.js +48 -0
  12. package/cbb/lib/install/tool-selection.js +45 -0
  13. package/cbb/lib/install/trae.js +43 -0
  14. package/cbb/lib/install/workbuddy.js +36 -0
  15. package/cbb/lib/install/workspaces.js +21 -17
  16. package/cbb/lib/openspec/index.js +337 -554
  17. package/cbb/lib/superpowers/index.js +246 -265
  18. package/cbb/lib/utils/check-update.js +17 -2
  19. package/cbb/lib/utils/githook.js +226 -0
  20. package/cbb/lib/utils/gitignore.js +16 -3
  21. package/cbb/lib/utils/plugin-hook.js +92 -0
  22. package/cbb/lib/utils/settings.js +17 -4
  23. package/cbb/lib/utils/tar.js +92 -0
  24. package/cbb/lib/utils/upstream.js +90 -0
  25. package/cbb/worktrees/_shared/scripts/check-local-state.js +114 -0
  26. package/cbb/worktrees/_shared/scripts/find-target-worktree.js +148 -96
  27. package/cbb/worktrees/_shared/scripts/find-workspace-root.js +7 -7
  28. package/cbb/worktrees/_shared/scripts/install-git-hook.js +70 -0
  29. package/cbb/worktrees/_shared/scripts/junction-ai-config.js +127 -0
  30. package/cbb/worktrees/_shared/scripts/start-local.js +180 -0
  31. package/cbb/worktrees/skills/cbb-local-mode/SKILL.md +158 -0
  32. package/cbb/worktrees/skills/cbb-local-mode/scripts/check-local-state.js +114 -0
  33. package/cbb/worktrees/skills/cbb-local-mode/scripts/install-git-hook.js +70 -0
  34. package/cbb/worktrees/skills/cbb-local-mode/scripts/silence-popup.js +23 -0
  35. package/cbb/worktrees/skills/cbb-local-mode/scripts/start-local.js +180 -0
  36. package/cbb/worktrees/skills/cbb-worktree-close/SKILL.md +60 -43
  37. package/cbb/worktrees/skills/cbb-worktree-close/scripts/check-env.js +3 -3
  38. package/cbb/worktrees/skills/cbb-worktree-close/scripts/check-unarchived.js +75 -48
  39. package/cbb/worktrees/skills/cbb-worktree-close/scripts/delete-branches.js +1 -1
  40. package/cbb/worktrees/skills/cbb-worktree-close/scripts/discover-apps.js +14 -5
  41. package/cbb/worktrees/skills/cbb-worktree-close/scripts/find-target-worktree.js +148 -96
  42. package/cbb/worktrees/skills/cbb-worktree-close/scripts/find-workspace-root.js +7 -7
  43. package/cbb/worktrees/skills/cbb-worktree-close/scripts/remove-worktrees.js +13 -4
  44. package/cbb/worktrees/skills/cbb-worktree-close/scripts/safety-check.js +11 -2
  45. package/cbb/worktrees/skills/cbb-worktree-init/SKILL.md +138 -129
  46. package/cbb/worktrees/skills/cbb-worktree-init/scripts/check-env.js +1 -1
  47. package/cbb/worktrees/skills/cbb-worktree-init/scripts/check-repos.js +2 -2
  48. package/cbb/worktrees/skills/cbb-worktree-init/scripts/create-branches.js +33 -8
  49. package/cbb/worktrees/skills/cbb-worktree-init/scripts/create-worktrees.js +60 -46
  50. package/cbb/worktrees/skills/cbb-worktree-init/scripts/find-workspace-root.js +7 -7
  51. package/cbb/worktrees/skills/cbb-worktree-init/scripts/install-git-hook.js +70 -0
  52. package/cbb/worktrees/skills/cbb-worktree-init/scripts/junction-ai-config.js +127 -0
  53. package/cbb/worktrees/skills/cbb-worktree-init/scripts/parse-config.js +7 -7
  54. package/cbb/worktrees/skills/cbb-worktree-init/scripts/push-branches.js +3 -3
  55. package/cbb/worktrees/skills/cbb-worktree-init/scripts/sync-repos.js +11 -7
  56. package/cbb/worktrees/skills/cbb-worktree-init/scripts/update-gitignore.js +37 -9
  57. package/cbb/worktrees/skills/cbb-worktree-push/SKILL.md +140 -71
  58. package/cbb/worktrees/skills/cbb-worktree-push/scripts/commit-worktrees.js +70 -12
  59. package/cbb/worktrees/skills/cbb-worktree-push/scripts/find-target-worktree.js +148 -96
  60. package/cbb/worktrees/skills/cbb-worktree-push/scripts/find-workspace-root.js +7 -7
  61. package/cbb/worktrees/skills/cbb-worktree-push/scripts/make-mr-urls.js +211 -0
  62. package/cbb/worktrees/skills/cbb-worktree-push/scripts/push-worktrees.js +67 -20
  63. package/config/{workspace-config.sample.json → CBB-WORKSPACE.sample.json} +1 -1
  64. package/config/openspec/schemas/spec-driven/schema.yaml +40 -30
  65. package/config/openspec/schemas/spec-driven/templates/design.md +0 -18
  66. package/config/openspec/schemas/spec-driven/templates/proposal.md +4 -3
  67. package/config/upstream-mirrors.json +17 -0
  68. package/config/workspace-agents.sample.md +4 -33
  69. package/external/frontend-design/.version +6 -0
  70. package/external/frontend-design/LICENSE.txt +177 -0
  71. package/external/frontend-design/SKILL.md +71 -0
  72. package/openspec/.version +2 -3
  73. package/openspec/commands/apply.md +189 -175
  74. package/openspec/commands/archive.md +244 -216
  75. package/openspec/commands/bulk-archive.md +362 -327
  76. package/openspec/commands/continue.md +115 -105
  77. package/openspec/commands/explore.md +230 -199
  78. package/openspec/commands/ff.md +115 -104
  79. package/openspec/commands/new.md +74 -63
  80. package/openspec/commands/onboard.md +562 -548
  81. package/openspec/commands/propose.md +161 -150
  82. package/openspec/commands/sync.md +277 -249
  83. package/openspec/commands/update.md +98 -80
  84. package/openspec/commands/verify.md +206 -162
  85. package/openspec/skills/openspec-apply-change/SKILL.md +20 -5
  86. package/openspec/skills/openspec-archive-change/SKILL.md +40 -11
  87. package/openspec/skills/openspec-bulk-archive-change/SKILL.md +46 -9
  88. package/openspec/skills/openspec-continue-change/SKILL.md +14 -3
  89. package/openspec/skills/openspec-explore/SKILL.md +22 -10
  90. package/openspec/skills/openspec-ff-change/SKILL.md +15 -3
  91. package/openspec/skills/openspec-new-change/SKILL.md +13 -1
  92. package/openspec/skills/openspec-onboard/SKILL.md +58 -43
  93. package/openspec/skills/openspec-propose/SKILL.md +15 -3
  94. package/openspec/skills/openspec-sync-specs/SKILL.md +31 -2
  95. package/openspec/skills/openspec-update-change/SKILL.md +34 -15
  96. package/openspec/skills/openspec-verify-change/SKILL.md +72 -26
  97. package/package.json +3 -2
  98. package/superpowers/.version +4 -4
  99. package/superpowers/skills/brainstorming/SKILL.md +47 -12
  100. package/superpowers/skills/brainstorming/scripts/frame-template.html +213 -213
  101. package/superpowers/skills/brainstorming/scripts/server.cjs +723 -723
  102. package/superpowers/skills/brainstorming/visual-companion.md +6 -6
  103. package/superpowers/skills/diagnosing-superpowers/SKILL.md +120 -0
  104. package/superpowers/skills/diagnosing-superpowers/prompts/analyst-common.md +38 -0
  105. package/superpowers/skills/diagnosing-superpowers/prompts/cost-and-time.md +28 -0
  106. package/superpowers/skills/diagnosing-superpowers/prompts/plan-adherence.md +29 -0
  107. package/superpowers/skills/diagnosing-superpowers/prompts/quality-evidence.md +26 -0
  108. package/superpowers/skills/diagnosing-superpowers/prompts/repeated-work.md +30 -0
  109. package/superpowers/skills/diagnosing-superpowers/prompts/request-conflicts.md +20 -0
  110. package/superpowers/skills/diagnosing-superpowers/prompts/scrub-audit.md +33 -0
  111. package/superpowers/skills/diagnosing-superpowers/prompts/scrub.md +29 -0
  112. package/superpowers/skills/diagnosing-superpowers/prompts/similar-session.md +38 -0
  113. package/superpowers/skills/diagnosing-superpowers/prompts/skill-timeline.md +30 -0
  114. package/superpowers/skills/diagnosing-superpowers/prompts/stumbles.md +28 -0
  115. package/superpowers/skills/diagnosing-superpowers/references/context-safety.md +22 -0
  116. package/superpowers/skills/diagnosing-superpowers/references/github-issues.md +47 -0
  117. package/superpowers/skills/diagnosing-superpowers/references/redaction-policy.md +34 -0
  118. package/superpowers/skills/diagnosing-superpowers/references/session-discovery.md +31 -0
  119. package/superpowers/skills/diagnosing-superpowers/templates/bundle-README.md +77 -0
  120. package/superpowers/skills/diagnosing-superpowers/templates/case.md +64 -0
  121. package/superpowers/skills/diagnosing-superpowers/templates/issue.md +51 -0
  122. package/superpowers/skills/diagnosing-superpowers/templates/report.md +82 -0
  123. package/superpowers/skills/executing-plans/SKILL.md +350 -41
  124. package/superpowers/skills/executing-plans/scripts/task-done +52 -0
  125. package/superpowers/skills/executing-plans/scripts/task-start +28 -0
  126. package/superpowers/skills/requesting-code-review/SKILL.md +1 -1
  127. package/superpowers/skills/requesting-code-review/code-reviewer.md +17 -0
  128. package/superpowers/skills/subagent-driven-development/SKILL.md +18 -18
  129. package/superpowers/skills/subagent-driven-development/re-review-prompt.md +1 -1
  130. package/superpowers/skills/subagent-driven-development/scripts/review-package +53 -46
  131. package/superpowers/skills/subagent-driven-development/scripts/sdd-workspace +82 -40
  132. package/superpowers/skills/subagent-driven-development/scripts/task-brief +43 -41
  133. package/superpowers/skills/subagent-driven-development/task-reviewer-prompt.md +2 -2
  134. package/superpowers/skills/systematic-debugging/root-cause-tracing.md +1 -1
  135. package/superpowers/skills/test-driven-development/SKILL.md +10 -0
  136. package/superpowers/skills/using-superpowers/SKILL.md +2 -0
  137. package/superpowers/skills/using-superpowers/references/claude-code-tools.md +29 -0
  138. package/superpowers/skills/using-superpowers/references/muse-tools.md +35 -0
  139. package/superpowers/skills/writing-plans/SKILL.md +62 -29
  140. package/superpowers/skills/writing-skills/SKILL.md +4 -2
  141. package/superpowers/skills/writing-skills/graphviz-conventions.dot +171 -171
  142. package/cbb/worktrees/commands/worktree-close.md +0 -64
  143. package/cbb/worktrees/commands/worktree-init.md +0 -51
  144. package/cbb/worktrees/commands/worktree-push.md +0 -42
  145. package/superpowers/skills/writing-plans/plan-document-reviewer-prompt.md +0 -49
@@ -0,0 +1,180 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * 本地模式开工(ADR 0002):根 + 指定应用就地切需求分支。
4
+ *
5
+ * 用法:node start-local.js <ws_root> <branch> [--apps app1,app2] [--all-apps]
6
+ *
7
+ * 行为:
8
+ * 1. 前置自检:根与所有 .codespace/<app>/ 若任一检出在非主干分支 → 遗留态,
9
+ * 退出码 2(agent 应升级为用户决策:继续 / 收尾 / 升级 / 丢弃,不在本脚本内处理)
10
+ * 2. 参与仓库 = workspace + 应用子集:
11
+ * - --apps a,b → 指定应用(本地模式推荐:从需求语境提取涉及应用)
12
+ * - --all-apps → CBB-WORKSPACE.json 全量应用
13
+ * - 缺省 → 仅 workspace(后续重跑幂等补齐,与 worktree-init 增补语义一致)
14
+ * 3. 每个仓库:
15
+ * - 当前已在 <branch> → reused
16
+ * - 分支存在(未检出)→ git checkout
17
+ * - 否则 → git checkout -b <branch> <remote>/<main>(基线不取 HEAD,ADR 0002;
18
+ * remote 不可用回退本地 main/master,再回退 HEAD)
19
+ * - dirty 仓库切换失败 → 报错退出(不 stash 兜底,交给用户决策)
20
+ * 4. 参与的应用仓库幂等补装提交防线 hook(cbb setup 已装,此处兜底)
21
+ *
22
+ * 幂等:重跑同一 branch 安全(reused / checkout 跳过)。
23
+ *
24
+ * 退出码:
25
+ * 0 - 全部切换成功
26
+ * 1 - 参数错误
27
+ * 2 - 遗留态 / 部分切换失败
28
+ */
29
+
30
+ const { execSync } = require('child_process');
31
+ const fs = require('fs');
32
+ const path = require('path');
33
+ const { installTo: installGitHook } = require('./install-git-hook');
34
+
35
+ const wsRoot = process.argv[2];
36
+ const branch = process.argv[3];
37
+
38
+ if (!wsRoot || !branch) {
39
+ console.error('Usage: node start-local.js <ws_root> <branch> [--apps a,b] [--all-apps]');
40
+ process.exit(1);
41
+ }
42
+
43
+ function run(cmd, cwd) {
44
+ try {
45
+ return { ok: true, out: execSync(cmd, { cwd, encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'] }).toString().trim() };
46
+ } catch (e) {
47
+ return { ok: false, err: (e.stderr ? e.stderr.toString() : e.message || String(e)).trim() };
48
+ }
49
+ }
50
+
51
+ /** 主干名:symbolic-ref 远端 HEAD → 本地 main/master 回退 */
52
+ function detectMain(cwd) {
53
+ const remotes = run('git remote', cwd);
54
+ if (remotes.ok && remotes.out) {
55
+ const r = remotes.out.split(/\r?\n/)[0];
56
+ const sym = run('git symbolic-ref "refs/remotes/' + r + '/HEAD"', cwd);
57
+ if (sym.ok && sym.out) return { remote: r, main: sym.out.replace('refs/remotes/' + r + '/', '') };
58
+ }
59
+ if (run('git rev-parse --verify --quiet refs/heads/main', cwd).ok) return { remote: null, main: 'main' };
60
+ if (run('git rev-parse --verify --quiet refs/heads/master', cwd).ok) return { remote: null, main: 'master' };
61
+ return { remote: null, main: null };
62
+ }
63
+
64
+ function currentBranch(cwd) {
65
+ const r = run('git branch --show-current', cwd);
66
+ return r.ok ? r.out : '';
67
+ }
68
+
69
+ // ── 1. 前置自检:遗留态检测 ───────────────────────────────
70
+ // 「非主干」且「不是本次 branch」才是遗留(别的需求没收尾);
71
+ // 已检出在本次 branch 上 = 幂等补齐场景,放行(reused)。
72
+
73
+ function isOnMain(cwd) {
74
+ const info = detectMain(cwd);
75
+ return info.main ? currentBranch(cwd) === info.main : false;
76
+ }
77
+
78
+ function isOnTarget(cwd) {
79
+ return currentBranch(cwd) === branch || isOnMain(cwd);
80
+ }
81
+
82
+ const leftovers = [];
83
+ if (!isOnTarget(wsRoot)) leftovers.push({ name: 'workspace', branch: currentBranch(wsRoot) });
84
+ const codeDir = path.join(wsRoot, '.codespace');
85
+ if (fs.existsSync(codeDir)) {
86
+ for (const name of fs.readdirSync(codeDir).sort()) {
87
+ const appDir = path.join(codeDir, name);
88
+ if (fs.existsSync(path.join(appDir, '.git')) && !isOnTarget(appDir)) {
89
+ leftovers.push({ name, branch: currentBranch(appDir) });
90
+ }
91
+ }
92
+ }
93
+ if (leftovers.length > 0) {
94
+ console.error('❌ 遗留态:以下仓库检出在非主干分支,先由用户决策处理(继续/收尾/升级/丢弃):');
95
+ leftovers.forEach(l => console.error(' - ' + l.name + ': ' + l.branch));
96
+ console.log('---');
97
+ console.log('LOCAL_START_FAILED', JSON.stringify({ reason: 'leftover', leftovers }));
98
+ process.exit(2);
99
+ }
100
+
101
+ // ── 2. 参与仓库集合 ───────────────────────────────────────
102
+
103
+ let appNames = [];
104
+ const argApps = process.argv.indexOf('--apps');
105
+ const argAll = process.argv.includes('--all-apps');
106
+ if (argApps >= 0) {
107
+ const raw = process.argv[argApps + 1] || '';
108
+ appNames = raw.split(',').map(s => s.trim()).filter(Boolean);
109
+ } else if (argAll) {
110
+ try {
111
+ const config = JSON.parse(fs.readFileSync(path.join(wsRoot, 'CBB-WORKSPACE.json'), 'utf-8'));
112
+ appNames = (config.apps || []).map(a => a.name);
113
+ } catch (e) {
114
+ console.error('❌ 读取 CBB-WORKSPACE.json 失败:' + e.message);
115
+ process.exit(2);
116
+ }
117
+ }
118
+
119
+ const targets = [{ name: 'workspace', dir: wsRoot }];
120
+ for (const name of appNames) {
121
+ const appDir = path.join(wsRoot, '.codespace', name);
122
+ if (!fs.existsSync(path.join(appDir, '.git'))) {
123
+ console.error('❌ 应用仓库不存在:.codespace/' + name + '(请检查 CBB-WORKSPACE.json 或先跑 cbb setup)');
124
+ process.exit(2);
125
+ }
126
+ targets.push({ name, dir: appDir });
127
+ }
128
+
129
+ // ── 3. 逐仓库切换 ─────────────────────────────────────────
130
+
131
+ const results = [];
132
+ let hasFailure = false;
133
+
134
+ for (const t of targets) {
135
+ const cur = currentBranch(t.dir);
136
+ if (cur === branch) {
137
+ console.log('⏭️ ' + t.name + ': 已在 ' + branch + '(reused)');
138
+ results.push({ name: t.name, status: 'reused' });
139
+ continue;
140
+ }
141
+
142
+ const exists = run('git rev-parse --verify --quiet refs/heads/' + branch, t.dir).ok;
143
+ const info = detectMain(t.dir);
144
+ let cmd;
145
+ let base = 'HEAD';
146
+ if (exists) {
147
+ cmd = 'git checkout "' + branch + '"';
148
+ base = '(existing)';
149
+ } else {
150
+ if (info.remote && info.main) base = info.remote + '/' + info.main;
151
+ else if (info.main) base = info.main;
152
+ cmd = 'git checkout -b "' + branch + '" "' + base + '"';
153
+ }
154
+ const r = run(cmd, t.dir);
155
+ if (r.ok) {
156
+ console.log('✅ ' + t.name + ': ' + (exists ? 'checkout' : 'created from ' + base) + ' → ' + branch);
157
+ results.push({ name: t.name, status: exists ? 'switched' : 'created', base });
158
+ } else {
159
+ console.error('❌ ' + t.name + ': 切换失败 — ' + r.err);
160
+ results.push({ name: t.name, status: 'failed', error: r.err });
161
+ hasFailure = true;
162
+ }
163
+ }
164
+
165
+ // ── 4. 参与应用补装提交防线 hook(幂等)────────────────────
166
+
167
+ const hookResults = [];
168
+ for (const t of targets) {
169
+ if (t.name === 'workspace') continue; // 根仓库 hook 由 cbb setup 安装
170
+ hookResults.push({ name: t.name, status: installGitHook(t.dir) });
171
+ }
172
+
173
+ console.log('---');
174
+ console.log('LOCAL_START_RESULT', JSON.stringify({
175
+ branch,
176
+ results,
177
+ hooks: hookResults,
178
+ worktreeHint: '如需升级隔离:重跑 cbb-worktree-init(分支已存在自动复用,worktree 从分支创建,提交随身带走)',
179
+ }));
180
+ process.exit(hasFailure ? 2 : 0);
@@ -1,12 +1,6 @@
1
1
  ---
2
2
  name: cbb-worktree-close
3
3
  description: "关闭/清理/删除需求工作空间的 worktree 和分支。触发:用户明确说关闭/清理/删除 worktree、结束隔离开发、合并完要清理。禁止在 cbb-worktree-init 执行后自动调用——只有用户主动要求关闭时才触发。"
4
- license: MIT
5
- compatibility: Requires git (>=2.30) and Node.js (>=16.7.0). All commands are pure Node.js scripts under `scripts/`, fully cross-platform (Windows / macOS / Linux, bash / PowerShell / Git Bash).
6
- metadata:
7
- author: Contributors
8
- version: "2.0"
9
- generatedBy: coding-bb
10
4
  ---
11
5
 
12
6
  # Close Worktree(跨平台脚本化版本 v2.0)
@@ -18,10 +12,10 @@ metadata:
18
12
  ## 核心原则
19
13
 
20
14
  - 本技能的所有原子操作封装为 `scripts/*.js` Node.js 脚本,**跨平台执行**
21
- - 本技能是 `cbb-worktree-init` 的逆操作:共用 `workspace-config.json`、共用 `.worktrees/worktree-<需求名>/` 物理直聚命名、共用统一的分支名(不加前缀)
22
- - App worktree **物理嵌套**在 workspace worktree 内(`.worktrees/worktree-<需求名>/<应用名>/`),但 git 上属于各自仓库的独立 worktree
15
+ - 本技能是 `cbb-worktree-init` 的逆操作:共用 `CBB-WORKSPACE.json`、共用 `.worktrees/worktree-<需求名>/` 物理直聚命名、共用统一的分支名(不加前缀)
16
+ - App worktree **物理嵌套**在 workspace worktree 的 `.codespace/` 容器内(`.worktrees/worktree-<需求名>/.codespace/<应用名>/`,镜像空间根布局,ADR 0003),但 git 上属于各自仓库的独立 worktree
23
17
  - 只操作 `.worktrees/worktree-<需求名>` 这一类精确路径,**绝不碰其它 worktree**(如 IDE/工具自动创建的 `~/.qoder/worktree/...` 会话 worktree)
24
- - 通过检查 `.worktrees/worktree-<需求名>/<应用名>/` **是否实际存在**来确定关联应用——创建时用户可能只关联了 `workspace-config.json` 中的部分应用,不能假设全部应用都关联
18
+ - 通过检查 `.worktrees/worktree-<需求名>/.codespace/<应用名>/` **是否实际存在**来确定关联应用——创建时用户可能只关联了 `CBB-WORKSPACE.json` 中的部分应用,不能假设全部应用都关联
25
19
  - **顺序硬约束:先移除所有 worktree,再删除分支。** 目标分支当前正检出于 worktree 中,先删分支会报 `error: Cannot delete branch '<分支>' checked out at '<worktree 路径>'`
26
20
  - **破坏性操作必须先报告、经用户确认才执行**:未提交修改、未合并/未推送的提交,绝不静默 `--force` 或 `-D` 丢弃
27
21
  - 软性检查失败时报告详情,询问用户是否继续
@@ -29,12 +23,28 @@ metadata:
29
23
 
30
24
  ---
31
25
 
26
+ ## 调用脚本的统一方式
27
+
28
+ **重要**:本技能的所有脚本位于**本 SKILL.md 所在目录的 `scripts/` 下**(skill 目录)。
29
+
30
+ 调用时用**本技能目录的绝对路径**拼接脚本名(AI 读取本 SKILL.md 时即已知该目录),例如:
31
+
32
+ ```bash
33
+ node "<skill_dir>/scripts/<name>.js" <参数>
34
+ ```
35
+
36
+ > - `<skill_dir>` = 本 SKILL.md 所在目录的绝对路径(AI 从读取路径即可推出)
37
+ > - **cwd 必须保持在工作空间根目录**:脚本按 cwd 定位 `CBB-WORKSPACE.json` 与 git 仓库
38
+ > - 不要用 `./scripts/...` 裸相对路径(AI 的 cwd 不是技能目录,会解析失败)
39
+
40
+ ---
41
+
32
42
  ## Step 0:前置校验
33
43
 
34
44
  ### 0.1 环境健康探活
35
45
 
36
46
  ```bash
37
- node "D:/workspace/<ws_root>/.qoder/skills/cbb-worktree-close/scripts/check-env.js"
47
+ node "<skill_dir>/scripts/check-env.js"
38
48
  ```
39
49
 
40
50
  **解读输出**:
@@ -45,16 +55,16 @@ node "D:/workspace/<ws_root>/.qoder/skills/cbb-worktree-close/scripts/check-env.
45
55
  ### 0.2 定位工作空间根目录
46
56
 
47
57
  ```bash
48
- node "D:/workspace/<ws_root>/.qoder/skills/cbb-worktree-close/scripts/find-workspace-root.js"
58
+ node "<skill_dir>/scripts/find-workspace-root.js"
49
59
  ```
50
60
 
51
61
  **解读输出**:
52
62
  - 退出码 0 + 末行(纯路径) → WS_ROOT 绝对路径
53
63
  - 退出码 1 → 未找到工作空间根目录,**终止流程**
54
64
 
55
- > ⓘ **为什么不用 workspace-config.json 存在性判断**:init-worktree 会把 workspace-config.json 提交到主分支,`git worktree add` 会自动 checkout 副本到 worktree 目录中。若用 workspace-config.json 存在性判断,在 worktree 内执行时会错误地把 worktree 目录当作 WS_ROOT。使用 `git worktree list` 的第一行(git 契约上始终为主仓库绝对路径)则不受该问题影响。
65
+ > ⓘ **为什么不用 CBB-WORKSPACE.json 存在性判断**:init-worktree 会把 CBB-WORKSPACE.json 提交到主分支,`git worktree add` 会自动 checkout 副本到 worktree 目录中。若用 CBB-WORKSPACE.json 存在性判断,在 worktree 内执行时会错误地把 worktree 目录当作 WS_ROOT。使用 `git worktree list` 的第一行(git 契约上始终为主仓库绝对路径)则不受该问题影响。
56
66
 
57
- > close-worktree 是 init-worktree 的逆操作,但本步骤的 WS_ROOT 定位逻辑与 init-worktree 不同:init-worktree 强制根目录执行(不需要处理 worktree 场景),close-worktree 支持 worktree 内执行(需要绕开 workspace-config.json 副本陷阱)。
67
+ > close-worktree 是 init-worktree 的逆操作,但本步骤的 WS_ROOT 定位逻辑与 init-worktree 不同:init-worktree 强制根目录执行(不需要处理 worktree 场景),close-worktree 支持 worktree 内执行(需要绕开 CBB-WORKSPACE.json 副本陷阱)。
58
68
 
59
69
  ---
60
70
 
@@ -69,7 +79,7 @@ node "D:/workspace/<ws_root>/.qoder/skills/cbb-worktree-close/scripts/find-works
69
79
  3. AI agent 列出 `.worktrees/` 下的所有 worktree:
70
80
 
71
81
  ```bash
72
- node "D:/workspace/<ws_root>/.qoder/skills/cbb-worktree-close/scripts/find-target-worktree.js "<ws_root>"
82
+ node "<skill_dir>/scripts/find-target-worktree.js "<ws_root>"
73
83
  ```
74
84
 
75
85
  > 注:本技能支持工作空间根目录或 worktree 内执行,find-target-worktree 自动从 cwd 反推或列出。
@@ -84,29 +94,30 @@ node "D:/workspace/<ws_root>/.qoder/skills/cbb-worktree-close/scripts/find-targe
84
94
  ### 1.2 发现关联应用 worktree
85
95
 
86
96
  ```bash
87
- node "D:/workspace/<ws_root>/.qoder/skills/cbb-worktree-close/scripts/discover-apps.js "<ws_root>" "<branch>"
97
+ node "<skill_dir>/scripts/discover-apps.js "<ws_root>" "<branch>"
88
98
  ```
89
99
 
90
- 脚本自动从 `workspace-config.json` 读取应用列表,扫描 `.worktrees/worktree-<branch>/<应用名>/` 实际存在的子目录。
100
+ 脚本自动从 `CBB-WORKSPACE.json` 读取应用列表,扫描 `.worktrees/worktree-<branch>/.codespace/<应用名>/`(兼容 ≤beta.23 旧平铺 `<应用名>/`)实际存在的子目录。
91
101
 
92
102
  **解读末行 `DISCOVER_RESULT`**:
93
103
  - `workspace` 字段:workspace worktree 绝对路径(AI agent 记录供后续命令使用)
94
104
  - `apps` 数组:实际存在的应用 worktree 列表(每项含 `name`, `repo`, `side`, `wt_path`, `app_path`)
95
105
 
96
- > 说明:app worktree 物理嵌套在 workspace worktree 内,移除 workspace worktree 时嵌套的子目录会一并消失,但各 app 仓库的 git worktree 元数据(`.git/worktrees/`)仍需通过 `git worktree remove` 逐个清理。AI 配置目录(`.claude/`、`.qoder/`、`.cbb/`)在每个 worktree 内由 init-worktree 独立安装,随 worktree 目录删除而清理,skill 无需特殊处理。
106
+ > 说明:app worktree 物理嵌套在 workspace worktree 的 `.codespace/` 容器内,移除 workspace worktree 时嵌套的子目录会一并消失,但各 app 仓库的 git worktree 元数据(`.git/worktrees/`)仍需通过 `git worktree remove` 逐个清理。AI 配置经 junction 链入 worktree(只读内容子目录指向空间根唯一物理副本),随 worktree 目录一并消失,无需特殊处理。
97
107
 
98
108
  ### 1.3 检查未归档 change
99
109
 
100
110
  ```bash
101
- node "D:/workspace/<ws_root>/.qoder/skills/cbb-worktree-close/scripts/check-unarchived.js "<ws_root>"
111
+ node "<skill_dir>/scripts/check-unarchived.js "<ws_root>" "<branch>"
102
112
  ```
103
113
 
104
- 脚本扫描 `openspec/changes/` 目录,筛选出非 `archive/` 子目录的 change 名称列表。
114
+ 脚本**双扫**(ADR 0002):空间根当前检出的 `openspec/changes/`(本地模式产物)与 `.worktrees/worktree-<branch>/openspec/changes/`(worktree 模式产物),返回未归档 change 名称合集。
105
115
 
106
116
  **解读末行 `UNARCHIVED_RESULT`**:
107
- - `changes`:未归档 change 名称数组(空数组 = 全部已归档)
108
- - 退出码 0 → 检查完成(无论是否有未归档 change)
109
- - 退出码 1 → `openspec/changes/` 目录不存在,视为无未归档 change
117
+ - `changes`:未归档 change 名称合集(空数组 = 两处全部已归档)
118
+ - `detail`:每个 change 的来源(`root` / `worktree` / `both`)——Step 3.2 归档与 Step 4.1 执行归档时按来源定位目录
119
+ - 退出码 0 → 检查完成(无论是否有未归档 change;目录不存在视为该处无 change)
120
+ - 退出码 1 → 参数错误
110
121
 
111
122
  > ⓘ 本检查为信息收集性质,不阻断流程。结果将在 Step 3 用户决策时展示。
112
123
 
@@ -117,7 +128,7 @@ node "D:/workspace/<ws_root>/.qoder/skills/cbb-worktree-close/scripts/check-unar
117
128
  对 workspace worktree 和每个关联应用 worktree,依次执行三项检查并汇总成安全状态表。**这是破坏性操作前的必经环节,结果将驱动 Step 3 的用户决策。**
118
129
 
119
130
  ```bash
120
- node "D:/workspace/<ws_root>/.qoder/skills/cbb-worktree-close/scripts/safety-check.js "<ws_root>" "<branch>"
131
+ node "<skill_dir>/scripts/safety-check.js "<ws_root>" "<branch>"
121
132
  ```
122
133
 
123
134
  脚本内部对每个仓库自动执行:
@@ -201,10 +212,12 @@ AskUserQuestion({
201
212
 
202
213
  **归档执行逻辑**(用户选择归档后,在 Step 4 中依次对每个待归档 change 执行):
203
214
 
215
+ > 落点按 Step 1.3 `detail` 的来源决定:`root` → 在 `<ws_root>/openspec/changes/` 执行;`worktree` → 在 `.worktrees/worktree-<branch>/openspec/changes/` 执行;`both` → 先向用户确认以哪份为准(通常 worktree 内为最新)。
216
+
204
217
  1. `openspec status --change "<name>" --json` → 检查 artifacts 完成状态
205
218
  2. 读取 `tasks.md` → 检查任务完成状态
206
219
  3. 检查 `openspec/changes/<name>/specs/` → 判断 delta specs 是否需要同步
207
- 4. 执行归档(`mv openspec/changes/<name> openspec/changes/archive/YYYY-MM-DD-<name>`)
220
+ 4. 执行归档(`mv <来源目录>/openspec/changes/<name> <来源目录>/openspec/changes/archive/YYYY-MM-DD-<name>`)
208
221
 
209
222
  **逐项确认规则**:
210
223
  - 若某个 change 有 artifacts 或 tasks 未完成 → 对该 change 单独弹出确认:*"<name> 有 N 个任务未完成,仍要归档吗?"*
@@ -226,10 +239,10 @@ AskUserQuestion({
226
239
 
227
240
  ```bash
228
241
  # 仅删本地分支(已合并自动 -d,未合并自动 -D;含 squash 合并 -D 兜底)
229
- node "<skill>/scripts/delete-branches.js" "<ws_root>" "<branch>" local
242
+ node "<skill_dir>/scripts/delete-branches.js" "<ws_root>" "<branch>" local
230
243
 
231
244
  # 删本地 + 远程分支
232
- node "<skill>/scripts/delete-branches.js" "<ws_root>" "<branch>" local_remote
245
+ node "<skill_dir>/scripts/delete-branches.js" "<ws_root>" "<branch>" local_remote
233
246
  ```
234
247
 
235
248
  **调用前的强警告**(调用方需自评):
@@ -260,7 +273,7 @@ node "<skill>/scripts/delete-branches.js" "<ws_root>" "<branch>" local_remote
260
273
  [中止] 终止流程,我先手动处理
261
274
  ```
262
275
 
263
- - 用户选择「先推送」→ AI 引导用户执行 `cbb-worktree-push`(或 `/cbb:worktree-push`),完成后**重新跑** Step 2 safety-check 确认 `unpushed_count = 0` 再回到本步骤
276
+ - 用户选择「先推送」→ AI 引导用户执行 `cbb-worktree-push` skill(或 `/cbb-worktree-push`),完成后**重新跑** Step 2 safety-check 确认 `unpushed_count = 0` 再回到本步骤
264
277
  - 用户选择「接受丢失」→ 必须二次确认(ahead 提交真的会丢),确认后继续 Step 4
265
278
  - 用户选择「中止」→ 终止流程
266
279
 
@@ -289,14 +302,14 @@ node "<skill>/scripts/delete-branches.js" "<ws_root>" "<branch>" local_remote
289
302
  **顺序:先移除各应用 worktree,再移除 workspace worktree**(逆序于创建;也为后续删分支扫清"分支仍被检出"的障碍)。
290
303
 
291
304
  ```bash
292
- node "D:/workspace/<ws_root>/.qoder/skills/cbb-worktree-close/scripts/remove-worktrees.js "<ws_root>" "<branch>" "<force>"
305
+ node "<skill_dir>/scripts/remove-worktrees.js "<ws_root>" "<branch>" "<force>"
293
306
  ```
294
307
 
295
308
  **三层降级策略**:`git worktree remove` 实际包含两个动作——①清理 git 元数据(`.git/worktrees/`)②删除物理目录。当 IDE 占用目录时 ② 会被 Windows 文件锁阻塞,但 ① 永远不受影响。脚本将这两个动作解耦:
296
309
 
297
310
  - **第 1 层(正常路径):** `git worktree remove` 一步完成元数据 + 物理目录清理
298
311
  - **第 2 层(解耦清理):** 当目录被锁时,先 `prune` 清理 git 元数据(始终成功),再 `rmdir`/`rm -rf` 尝试物理删除
299
- - **第 3 层(延迟清理):** Windows 下 IDE 持有排他句柄时触发。元数据已清理保证分支删除不受影响;物理目录留给 init-worktree 下次创建时自愈(见 init Step 4.0)
312
+ - **第 3 层(延迟清理):** Windows 下 IDE 持有排他句柄时触发。元数据已清理保证分支删除不受影响;物理目录**不会被 init 自动删除**(0.0.3-beta.11 起 init 只 prune 元数据、绝不删目录,防误删活跃 worktree),需用户关闭 IDE 后手动删除,或重跑 close 补删
300
313
 
301
314
  **解读末行 `REMOVE_RESULT`**:
302
315
  - `has_locked: false` → 全部清理成功
@@ -310,21 +323,23 @@ node "D:/workspace/<ws_root>/.qoder/skills/cbb-worktree-close/scripts/remove-wor
310
323
 
311
324
  所有 worktree 已成功移除后,如果当前 shell 的工作目录位于**已被关闭的 worktree 内**,需要主动让 IDE 窗口切回 `WS_ROOT`,避免 IDE 窗口指向已删除的目录。
312
325
 
313
- > ⓘ **本节是 close-worktree 专属逻辑**:与 init-worktree 的 Step 5 不共享、不交叉同步。init-worktree 负责"创建后**在新窗口打开** worktree",本节负责"关闭后切回根目录",两者职责独立。
326
+ > ⓘ **本节是 close-worktree 专属逻辑**:与 init-worktree 的 Step 5 不共享、不交叉同步。init-worktree 负责"创建后把开发会话开进 worktree",本节负责"关闭后切回根目录",两者职责独立。
314
327
 
315
328
  **AI agent 行为**:
316
- 1. 检查当前 cwd 是否在被关闭的 worktree 内
317
- 2. 仅当匹配时,调用对应 IDE 的 `--reuse-window` 命令切回 WS_ROOT:
329
+ 1. 检查当前会话 / IDE 窗口是否指向被关闭的 worktree
330
+ 2. **OpenCode 等支持会话搬移的工具**:agent 亲自经 code-mode 调用 `opencode.session_move` 把会话切回 `<ws_root>`——自动完成、上下文保留、**不列入用户待办**,切完核对工作目录
331
+ 3. **Qoder / Cursor / VS Code**:调用对应 IDE 的 `--reuse-window` 命令切回 WS_ROOT:
318
332
  - Qoder → `qoder --reuse-window "<ws_root>"`
319
333
  - Cursor → `cursor --reuse-window "<ws_root>"`
320
334
  - VS Code → `code --reuse-window "<ws_root>"`
321
- 3. **JetBrains 跳过主动切换**——`idea64 <path>` 会启动新进程或提示"打开/在新窗口打开",让用户手动操作
335
+ 4. **JetBrains 跳过主动切换**——`idea64 <path>` 会启动新进程或提示"打开/在新窗口打开",让用户手动操作
322
336
 
323
337
  **关键点**:
324
- - **只在 pwd 匹配被关闭的 worktree 时触发**——如果 IDE 窗口本身在 WS_ROOT 内,或会话位于其他目录,再切一次是多余的
338
+ - **只在会话 / 窗口匹配被关闭的 worktree 时触发**——如果已在 WS_ROOT 内,再切一次是多余的
339
+ - **OpenCode 会话切回是零成本自动动作**——`session_move` 复用当前会话(上下文保留、无新窗口),agent 亲自做,严禁推给用户
325
340
  - **仅切 Qoder / Cursor / VS Code**——这三者的 `--reuse-window` 是轻量级"切目录",不重启 IDE
326
341
  - **不会产生孤立窗口**——`--reuse-window` 是复用当前窗口切换目录,**不会打开新窗口**。即使 IDE 已开着指向 WS_ROOT 的其他窗口,本步骤也不会重复创建,仅是把当前指向已删除目录的窗口切走
327
- - **失败不致命**——`--reuse-window` 失败时仅警告,后续分支删除不受影响
342
+ - **失败不致命**——`session_move` / `--reuse-window` 失败时仅警告,后续分支删除不受影响
328
343
 
329
344
  ### 4.4 分支处理(默认跳过,按需手动调用)
330
345
 
@@ -333,9 +348,9 @@ node "D:/workspace/<ws_root>/.qoder/skills/cbb-worktree-close/scripts/remove-wor
333
348
  如用户在 Step 1.1 选定 worktree 时已要求"删除分支",或运行后需要单独清理,可在此步骤**手动调用**:
334
349
 
335
350
  ```bash
336
- node "<skill>/scripts/delete-branches.js" "<ws_root>" "<branch>" local
351
+ node "<skill_dir>/scripts/delete-branches.js" "<ws_root>" "<branch>" local
337
352
  # 或
338
- node "<skill>/scripts/delete-branches.js" "<ws_root>" "<branch>" local_remote
353
+ node "<skill_dir>/scripts/delete-branches.js" "<ws_root>" "<branch>" local_remote
339
354
  ```
340
355
 
341
356
  脚本内部对每个仓库:
@@ -371,6 +386,8 @@ git -C "<ws_root>" worktree list
371
386
 
372
387
  目标 worktree 应已不在列表中。
373
388
 
389
+ **工作区回根交接(必须执行)**:若用户的开发会话工作在 worktree 内(当前会话工作目录 = 已删除的 `<wt_path>`),此刻目录已悬空——**OpenCode 等支持会话搬移的工具由 agent 自动 `session_move` 切回 `<ws_root>`,不列入用户待办**;无该能力的工具才提示用户把工作区切回 `<ws_root>`(打开文件夹 / Open Folder / 切换工作目录),并在最终报告中给出该绝对路径。
390
+
374
391
  ---
375
392
 
376
393
  ## 最终报告
@@ -380,16 +397,16 @@ git -C "<ws_root>" worktree list
380
397
  当前目录:<工作空间根目录绝对路径>
381
398
  已移除 worktree:
382
399
  - .worktrees/worktree-feature-xxx(workspace)
383
- - .worktrees/worktree-feature-xxx/ep-user-info-server(嵌套,ep-user-info-server 仓库)
384
- - .worktrees/worktree-feature-xxx/ep-user-management-app(嵌套,ep-user-management-app 仓库)
400
+ - .worktrees/worktree-feature-xxx/.codespace/ep-user-info-server(嵌套,ep-user-info-server 仓库)
401
+ - .worktrees/worktree-feature-xxx/.codespace/ep-user-management-app(嵌套,ep-user-management-app 仓库)
385
402
  未提交修改:已丢弃(--force)/ 无
386
403
  未归档 change 处理:
387
404
  ✅ ai-call-send-im-msg → 已归档到 archive/2026-07-08-ai-call-send-im-msg
388
405
  ⏭️ fix-payment-timeout → 用户选择跳过
389
406
  (无未归档 change 时不显示本节)
390
407
  分支处理:feature-xxx 已保留(默认行为;如需删除分支,手动调用 scripts/delete-branches.js)
391
- AI 配置:worktree 内独立安装,随 worktree 删除而清理;工作空间根目录的 AI 配置保留
392
408
  剩余工作空间 worktree:<列出仍存在的,或 "无" >
409
+ 👉 仅当所用工具不支持自动切根、且你的窗口仍停在已删除的 worktree 时,请手动切回工作空间根:<ws_root 绝对路径>
393
410
  Workspace cleaned up.
394
411
  ```
395
412
 
@@ -411,14 +428,14 @@ git -C "<ws_root>" worktree list
411
428
  - 未经用户确认用 `--force` 丢弃未提交修改,或主动调用 `delete-branches.js` 对未合并分支执行 `-D` 强删
412
429
  - 操作 `.worktrees/worktree-<需求名>` 以外的任何 worktree(如 `~/.qoder/worktree/...` 会话 worktree)
413
430
  - 移除/删除前跳过 Step 2 安全评估
414
- - 删除工作空间根目录的 AI 配置目录(`.claude/`、`.qoder/`、`.cbb/` 等源目录)
431
+ - 删除工作空间根目录的 AI 配置目录(`.claude/`、`.qoder/`、`.opencode/` 等)
415
432
  - 用 `AskUserQuestion` 收集自由文本(分支名等直接对话提问)
416
433
  - 手动改任何仓库的 `.gitignore`(关闭流程不碰它)
417
434
  - 运行环境 node 不可用时强行继续 → 改为 fail-fast 提示用户
418
435
 
419
436
  ### 必须执行
420
437
 
421
- - 读 `workspace-config.json` 取应用名列表;以 `.worktrees/worktree-<需求名>/<应用名>/` 实际存在性确定关联清单
438
+ - 读 `CBB-WORKSPACE.json` 取应用名列表;以 `.worktrees/worktree-<需求名>/.codespace/<应用名>/` 实际存在性确定关联清单
422
439
  - 移除/删除前执行安全评估(未提交修改 / 是否合并 / 是否推远程)并报告
423
440
  - 严格按 Step 4.2(移除 worktree)→ Step 4.4(删分支)顺序
424
441
  - 移除 workspace worktree 前先把会话移出(`cd "<WS_ROOT>" &&` 或 `git -C` 绝对路径)
@@ -429,7 +446,7 @@ git -C "<ws_root>" worktree list
429
446
  - **合并判定依赖刷新后的远程引用,故 Step 2.0 先主动 `git fetch`**:feature 多由他人经 MR 合入远端,本地引用否则会陈旧误判
430
447
  - **并行块用临时文件收结果、不用共享变量**:原 bash 版本中 `&` 后台任务跑在独立子 shell;Node.js 同步执行无此问题
431
448
  - **`.gitignore` 无需清理**:创建时的 `.worktrees/` 条目提交在 feature 分支上,删分支时随分支消失(未合并)或无害保留(已合并)
432
- - **AI 配置目录在每个 worktree 内独立安装**:关闭 worktree 时这些目录随 worktree 目录一起删除
449
+ - **AI 配置在 worktree 内是 junction(链接)**:物理副本只在空间根(单层安装),worktree 删除时链接随目录树消失,无需清理动作
433
450
  - **部分 `worktree remove` 失败**:已移除的无需回退,未移除的报告详情让用户处理后重跑
434
451
  - **Windows IDE 目录锁的三层降级**:`git worktree remove` 内部做两件事——①清元数据 ②删目录。Windows 文件锁只阻塞 ②,不阻塞 ①
435
452
 
@@ -3,14 +3,14 @@ require('./silence-popup');
3
3
  /**
4
4
  * cbb-worktree-close 环境健康探活
5
5
  *
6
- * close-worktree 必须在工作空间根目录或 worktree 内执行,且需找到 workspace-config.json。
7
- * 与 init/extend 探活同构,但额外校验 workspace-config.json 存在性。
6
+ * close-worktree 必须在工作空间根目录或 worktree 内执行,且需找到 CBB-WORKSPACE.json。
7
+ * 与 init/extend 探活同构,但额外校验 CBB-WORKSPACE.json 存在性。
8
8
  *
9
9
  * 退出码:
10
10
  * 0 - 探活通过
11
11
  * 1 - 致命错误(git 不可用)
12
12
  * 2 - 当前不在 git 仓库
13
- * 3 - 未找到 workspace-config.json
13
+ * 3 - 未找到 CBB-WORKSPACE.json
14
14
  */
15
15
 
16
16
  const { execSync } = require('child_process');
@@ -1,48 +1,75 @@
1
- #!/usr/bin/env node
2
- require('./silence-popup');
3
- /**
4
- * cbb-worktree-close 未归档 change 检查
5
- *
6
- * 用法:node check-unarchived.js <ws_root>
7
- *
8
- * 扫描 openspec/changes/ 目录,筛选出非 archive/ 子目录的 change 名称列表。
9
- * 本检查为信息收集性质,不阻断流程。
10
- *
11
- * 退出码:
12
- * 0 - 检查完成(无论是否有未归档 change)
13
- * 1 - 参数错误或 openspec/changes/ 目录不存在
14
- */
15
-
16
- const fs = require('fs');
17
- const path = require('path');
18
-
19
- const wsRoot = process.argv[2];
20
-
21
- if (!wsRoot) {
22
- console.error('Usage: node check-unarchived.js <ws_root>');
23
- process.exit(1);
24
- }
25
-
26
- const changesDir = path.join(wsRoot, 'openspec', 'changes');
27
-
28
- if (!fs.existsSync(changesDir)) {
29
- console.log('UNARCHIVED_RESULT {"changes":[]}');
30
- process.exit(0);
31
- }
32
-
33
- let entries;
34
- try {
35
- entries = fs.readdirSync(changesDir, { withFileTypes: true });
36
- } catch (e) {
37
- console.error('Failed to read openspec/changes/:', e.message);
38
- console.log('UNARCHIVED_RESULT {"changes":[]}');
39
- process.exit(0);
40
- }
41
-
42
- const unarchived = entries
43
- .filter(e => e.isDirectory() && e.name !== 'archive')
44
- .map(e => e.name)
45
- .sort();
46
-
47
- console.log('UNARCHIVED_RESULT', JSON.stringify({ changes: unarchived }));
48
- process.exit(0);
1
+ #!/usr/bin/env node
2
+ require('./silence-popup');
3
+ /**
4
+ * cbb-worktree-close 未归档 change 检查
5
+ *
6
+ * 用法:node check-unarchived.js <ws_root> [branch]
7
+ *
8
+ * 双扫(ADR 0002):
9
+ * - <ws_root>/openspec/changes/ → 空间根当前检出(本地模式产物 / 空间根遗留)
10
+ * - <ws_root>/.worktrees/worktree-<branch>/openspec/changes/ → worktree 模式产物
11
+ * (branch 未传时跳过 worktree 扫描,保持旧调用兼容)
12
+ *
13
+ * 输出:changes 为两处合集(去重);detail 标注每个 change 的来源
14
+ * ('root' / 'worktree',同名两处共存时为 'both')。
15
+ * 本检查为信息收集性质,不阻断流程。
16
+ *
17
+ * 退出码:
18
+ * 0 - 检查完成(无论是否有未归档 change)
19
+ * 1 - 参数错误
20
+ */
21
+
22
+ const fs = require('fs');
23
+ const path = require('path');
24
+
25
+ const wsRoot = process.argv[2];
26
+ const branch = process.argv[3];
27
+
28
+ if (!wsRoot) {
29
+ console.error('Usage: node check-unarchived.js <ws_root> [branch]');
30
+ process.exit(1);
31
+ }
32
+
33
+ /**
34
+ * 扫描单个 changes 目录,返回未归档 change 名数组(目录不存在返回 [])。
35
+ */
36
+ function scanDir(changesDir) {
37
+ if (!fs.existsSync(changesDir)) return [];
38
+ let entries;
39
+ try {
40
+ entries = fs.readdirSync(changesDir, { withFileTypes: true });
41
+ } catch (e) {
42
+ console.error('Failed to read ' + changesDir + ':', e.message);
43
+ return [];
44
+ }
45
+ return entries
46
+ .filter(e => e.isDirectory() && e.name !== 'archive')
47
+ .map(e => e.name);
48
+ }
49
+
50
+ const rootChanges = scanDir(path.join(wsRoot, 'openspec', 'changes'));
51
+
52
+ const worktreeDir = branch
53
+ ? path.join(wsRoot, '.worktrees', 'worktree-' + branch, 'openspec', 'changes')
54
+ : null;
55
+ const wtChanges = worktreeDir ? scanDir(worktreeDir) : [];
56
+
57
+ const detail = {};
58
+ for (const name of rootChanges) detail[name] = 'root';
59
+ for (const name of wtChanges) {
60
+ detail[name] = detail[name] === 'root' ? 'both' : 'worktree';
61
+ }
62
+
63
+ const changes = Object.keys(detail).sort();
64
+
65
+ if (rootChanges.length === 0 && wtChanges.length === 0) {
66
+ console.log('ℹ️ 未发现未归档 change(根与 worktree' + (worktreeDir ? '' : '(未指定 branch,跳过)') + '均无)');
67
+ } else {
68
+ console.log('ℹ️ 未归档 change:' + (changes.join(', ') || '无'));
69
+ for (const name of changes) {
70
+ console.log(' - ' + name + '(来源:' + detail[name] + ')');
71
+ }
72
+ }
73
+
74
+ console.log('UNARCHIVED_RESULT', JSON.stringify({ changes, detail }));
75
+ process.exit(0);
@@ -116,7 +116,7 @@ function deleteBranch(repoPath, name) {
116
116
  return true;
117
117
  }
118
118
 
119
- const configPath = path.join(wsRoot, 'workspace-config.json');
119
+ const configPath = path.join(wsRoot, 'CBB-WORKSPACE.json');
120
120
  let config = {};
121
121
  try { config = JSON.parse(fs.readFileSync(configPath, 'utf-8')); } catch (e) {}
122
122